目录
思路:
这道题让我们求非回文串的个数,但很不好求。所以,我们可以先求出回文串个数,再用总
排列个数减去回文串个数,就能得到非回文串个数。
总排列个数很容易求,即 n * (n - 1) * (n - 2) * ...... * 2 * 1。所以,这道题难点就在于如何求回
文串个数。
如果一个字符串是回文串。那么,它的左边也就等于它的右边。我们只需要求出一边的组合
个数,即 (n / 2) * (n / 2 - 1) * ...... * 2 * 1。就是回文串排列个数。
但是,如果有字母相等,又该怎么解决?
如果有字母相等,我们应求出其排列个数 (设其为 num) ,即 num * (num - 1) * ...... * 2 * 1。
但是,有可能会重复。举个例子:
假如有 4 个字母 b 。求出组合个数自然没有问题。但是,在之前,我们已经求出回文串组合
个数,就会出现被重复计算。所以,我们需要去除被重复计算部分,即出去 (num / 2) * (num / 2
- 1) * ...... * 2 * 1。读者可以自行思考为什么要出去这些数。
最后,在计算重复字母时,我们只需要计算 num * (num - 1) * ...... *(num / 2 + 2) *(num / 2 + 1)。
代码:
#include<bits/stdc++.h>
using namespace std;
long long lis[29]; //桶,记录字母出现个数
const long long maxn=1e9+7; //取模数字:
int main(){
long long n,ans=1;
char c;
cin>>n;
for(int i=1;i<=n;i++){
cin>>c;
lis[c-'a']++; //增加该字母个数
}
int ji=0,xb=0;
for(int j=0;j<26;j++) ji+=lis[j]%2; //计算出现奇数个字母个数
for(int i=2;i<=n;i++) ans=(ans*i)%maxn; //计算总排列个数
if(ji>1){ //如果奇数个字母个数超过一个,没有回文串,直接输出
cout<<ans;
return 0;
}
long long num=1;
for(int i=1;i<=n/2;i++) num=(num*i)%maxn; //计算回文串排列个数
for(int i=0;i<26;i++){
//计算字母相等个数
for(int j=lis[i]/2+1;j<=lis[i];j++) num=(num*j)%maxn;
}
ans=(ans-num+maxn)%maxn; //得到答案(ans-num可能为负数)
cout<<ans;
return 0;
}
谢谢大家的阅读!