您的位置:首页 > 其它

xiaoxin juju needs help

2016-03-27 13:31 351 查看
题意: 给你一个字符串(a-z),可以任意更改任意字符串的位置,但是不能删去任何字符串,得到的字符串必须是回文的,问你一共有多少种可能?

解法: 首先分析问题,你会发现如果字符串长度为偶数的话,且有字母出现了奇数次的话就输出0;如果字符串长度为奇数的话就一定要有且只有一个字母出现的次数为奇数,否则也输出0;

再认真分析下题意,你会发现排除了上述产生的情况,其实字符串长度为奇数和偶数是一样的情况,因为如果是奇数有一个字母为奇数的就一定在排列的正中间。

如果对上述情况了解了那就是一道很简单的排列组合问题了,首先要将各个字母出现的次数减半,然后对减半后的字母进行全排列注意去除重复的字母,例如输入的是aaaabbbbcc,先减半得到aabbc,对aabbc进行全排列为A(5,5)/(A(2,2)*A(2,2)),我当时比赛的时候就是这样写的,万万没想到如果数据非常大的时候有取模操作的话就会出现问题。

赛后想到了一种方法就是先不计算结果,可以用1到len/2的数组每位赋值为序列号,然后在去除重复字母的时候也是用数组保存相乘的每一位数,然后将每一位与前面保存的数进行约分,而且一定不会出现分数,最后将1-len/2的数组每一位进行相乘并且取模就可以了!

代码如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
#define LL long long
#define N 1005
const LL MOD  = 1000000007;
char str
;
int save
;
LL res;
map<char, int>m;
void init(int n)
{
for(int i=1; i<=n; i++)
save[i] = i;
}
int gcd(int n, int m)
{
return m==0 ? n : gcd(m, n%m);
}
int main()
{
//cout<<gcd(0,3)<<endl;
int T, len, odd;
cin>>T;
while(T--)
{
res = 1;
odd = 0;
cin>>str;
len = strlen(str);
m.clear();
for(int i=0; i<len; i++)
m[str[i]] ++;
for(char i='a'; i<='z'; i++)
if(m[i]%2)
odd++;
if(odd>1 || (odd == 1 && len%2==0))
cout<<0<<endl;
else if(len%2 == 0)
{
int total = 0;
for(char i='a'; i<='z'; i++)
{
m[i] = m[i] / 2;
total += m[i];
}
init(total);
for(char i='a'; i<='z'; i++)
{
while(m[i])
{
int mm = m[i];
for(int j=total; j>0; j--)
{
int tmp = gcd(save[j], m[i]);
save[j] = save[j] / tmp;
m[i] = m[i] / tmp;
}
m[i] = mm - 1;
}
}
for(int i=total; i>0; i--)
res = (res * save[i])%MOD;
cout<<res<<endl;
}
else
{
int total = 0;
for(char i='a'; i<='z'; i++)
{
m[i] = m[i] / 2;
total += m[i];
}
init(total);
for(char i='a'; i<='z'; i++)
{
while(m[i])
{
int mm = m[i];
for(int j=total; j>0; j--)
{
int tmp = gcd(save[j], m[i]);
save[j] = save[j] / tmp;
m[i] = m[i] / tmp;
}
m[i] = mm - 1;
}
}
for(int i=total; i>0; i--)
res = (res * save[i])%MOD;
cout<<res<<endl;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息