题目描述
Alice 有 �n 个字符,它们都是英文小写字母,从 1∼�1∼n 编号,分别为 �1,�2,…,��c1,c2,…,cn。
Bob 准备将这些字符重新排列,组成一个字符串 �S。Bob 知道 Alice 有强迫症,所以他打算将 �S 组成一个非回文串来折磨 Alice。
现在 Bob 想知道他共有多少种不同的排列字符的方案,能使得 �S 是个非回文串。一种排列字符的方案指的是一个 1∼�1∼n 的排列 ��pi,它所组成的 �=��1��2…���S=cp1cp2…cpn。
一个字符串是非回文串,当且仅当它的逆序串与原串不同。例如 abcda
的逆序串为 adcba
,与原串不同,故 abcda
是非回文串。而 abcba
的逆序串与原串相同,是回文串。
由于最后的结果可能很大,你只需要告诉 Bob 总方案数对 109+7109+7 取模后的值。
输入格式
第一行一个正整数 �n 表示字符个数。
第二行 �n 个英文小写字母 ��ci。
输出格式
仅一行一个整数表示答案。答案对 109+7109+7 取模。
输入输出样例
输入 #1
3 aba
输出 #1
4
输入 #2
8 aabbbbcc
输出 #2
39168
说明/提示
【数据范围】
对于 20%20% 的数据,n≤8;
对于 50%50% 的数据,n≤20;
另有 30%30% 的数据,字符只包含 a
和 b
;
对于 100%100% 的数据,3≤n≤2000。
思路:
对于这题,我们先假设相同字符是相同的。(相同字符之间不存在先后顺序)
则可以求出所有能构成字符串个数 res1(结合前置芝士)。
正难则反,我们可以求出能构成回文串的个数res2。
接下来是如何求解res2:
- n 为奇数且出现个数为奇数的字符不止一个或 n 为偶数且并不是全部字符出现个数为偶数时,res2=0。
- 如果不满足上面情况,则说明一定存在回文串。根据回文串的对称性,我们可以通过确定字符串的前半部分的状态,以此确定后半部分。而确定前半部分状态不存在任何限制,使用前置芝士即可求出 res2。
则在相同字符相同前提下的答案为 res1−res2res1−res2。
最后记得给相同字符全排列,最终答案为 (res1−res2)×∏(字母a...c出现个数)!(res1−res2)×∏(字母a...c出现个数)!。
时间复杂度 O(n)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int s[26];
int fac(int n){
long long cnt=1;
for(int i=1;i<=n;i++)cnt=(cnt*i)%mod;
return cnt;
}
int fact(int n){
long long cnt=1;
for(int i=n/2+1;i<=n;i++)cnt=(cnt*i)%mod;
return cnt;
}
signed main(){
int n;cin>>n;
for(int i=1;i<=n;i++){
char x;cin>>x;
s[x-'a']++;
}
int cnt=0,ans=fac(n);
for(int i=0;i<26;i++)if(s[i]%2==1)cnt++;
if(cnt>1){
cout<<ans;return 0;}
else{
long long cnt2=1;
for(int i=0;i<26;i++)cnt2=(cnt2*fact(s[i]))%mod;
cnt2=cnt2*fac(n/2)%mod;
cout<<(ans-cnt2+mod)%mod;
}
return 0;
}