P5684 [CSP-J2019 江西] 非回文串

题目描述

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=cp1​​cp2​​…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:

  1. n 为奇数且出现个数为奇数的字符不止一个或 n 为偶数且并不是全部字符出现个数为偶数时,res2=0。
  2. 如果不满足上面情况,则说明一定存在回文串。根据回文串的对称性,我们可以通过确定字符串的前半部分的状态,以此确定后半部分。而确定前半部分状态不存在任何限制,使用前置芝士即可求出 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; 
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值