题目链接:
hdu:http://acm.hdu.edu.cn/showproblem.php?pid=5651
bc:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=682&pid=1002
xiaoxin juju needs help
Accepts: 150
Submissions: 966
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
题解:
1、可行性:
统计每个字母出去的次数,如果有两种即以上字母出现的次数为奇数,则一定不可能排出回文串。
2、统计:
由于回文串左右两边必须相同,所以我们考虑一边就可以了(如果为奇数则正中间一个不管就和偶数情况是一样的了)。
则可以转化为排列组合问题,等价于求解: (cnt[i]代表某个字母出现的次数(cnt[i]>0) )
( )%(1e9+7)
由于涉及到除法,要求逆元:
例子:
求a/b(mod n) 如果b与n互质,则求满足bx=1(%n)的一个解x,原式可转化为a/b*bx(%n),即a*x(%n);这样就把除法消除了。
代码:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 1000 + 10; const int mod = 1e9 + 7; typedef long long LL; char str[maxn]; int n; int cnt[33]; int tmp[33], tot; LL b[maxn]; //预处理出阶乘 void pre() { b[0] = b[1] = 1; for (int i = 2; i < maxn; i++) b[i] = (b[i - 1] * i) % mod; } //扩展的欧几里得算法求逆元 void gcd(LL a, LL b, LL &d, LL &x, LL &y) { if (!b) { d = a; x = 1; y = 0; } else { gcd(b, a%b, d, y, x);//y=x',x=y'; //x=y'; y=x'-(a/b)*y'; y -= (a / b)*x; } } void init() { tot = 0; memset(cnt, 0, sizeof(cnt)); } int main() { pre(); int tc; scanf("%d", &tc); while (tc--) { init(); scanf("%s", str); n = strlen(str); for (int i = 0; i < strlen(str); i++) { cnt[str[i] - 'a']++; } int flag = 0; for (int i = 0; i < 33; i++) { if (cnt[i] % 2) flag++; //统计一半的字母出现的次数 if (cnt[i] > 0) { tmp[tot++] = cnt[i] / 2; } } if (flag > 1) { printf("0 "); continue; } LL sum = 1; for (int i = 0; i < tot; i++) { int t = tmp[i]; sum = (sum*b[t]) % mod; } LL d, x, y; gcd(sum, mod, d, x, y); //x有可能是负数,需要处理成正的。 x = (x%mod + mod) % mod; LL ans = (b[n / 2] * x) % mod; printf("%lld ", ans); } return 0; }