回文--nkoj3681

P3681  回文
时间限制 :  - MS    空间限制 :  65536 KB  评测说明 : 时限1000ms
问题描述

   给你一个由N个小写字母构成的字符串,请你将它改成“回文串”。你可以任意调整串中字符的位置,但不可以删掉其中的字符。问,最多能得到多少个不同的回文串?      

   结果可能很大,mod  1,000,000,007 后再输出!            

    例如给出字符串“aabb”      

    我们通过调整能得到两个回文串,分别是“abba”和”baab”

输入格式

一行,一个字符串

输出格式

一个整数,表示所求答案

样例输入

aabb

样例输出

2

提示

1<=N<=2000



注意到mod对于除法没有分配律,要使用乘法逆元

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define mod 1000000007
using namespace std;
long long cnt[30];
long long f[1000005];
long long niyuan[1000005];
long long supergcd(long long a,long long b,long long &x,long long &y){
	long long temp,r;
	if(b==0){x=1;y=0;return a;}
	r=supergcd(b,a%b,x,y);
	temp=x;x=y;y=temp-a/b*y;
	return r;
}
long long ni(long long a,long long n)
{
      long long x,y;
      if(supergcd(a,n,x,y)==1)return (x+n)%n;else return -1;
}
int main(){
	string s;
	cin>>s;
	long long t,len,i,odd=0,l=0,ans;
	len=s.length();
	for(i=0;i<len;i++){
		t=int(s[i])-int('a')+1;
		cnt[t]++;
	}
	for(i=1;i<=26;i++){
		if(cnt[i]&1)odd++;
		cnt[i]=cnt[i]>>1;
		l=l+cnt[i];
	}
	if(odd>1){
		cout<<"0";return 0;
	}
	f[0]=1;
	for(i=1;i<=l;i++)f[i]=f[i-1]*i%mod;
	for(i=1;i<=l;i++){
		niyuan[i]=ni(f[i],mod);
	}
	ans=f[l];`	
	for(i=1;i<=26;i++){
		if(cnt[i]==0)continue;
		ans=(ans*niyuan[cnt[i]])%mod;
	}
	cout<<ans%mod;
}
Solution 2 by spark
分析:
将所给字符串中i号字母出现的次数为cnt[i](a是1号);
(1)如果出现了奇数次的字母多于一个,那么一定无解。
(2)如果有解,对于回文串的一半而言,每个字母出现了 cnt[i]/2次,总长度是T=n/2。
那么方案书就是这些字母的组合方案数,由排列组合知识得:
ANS= T! / (cnt[1]/2)!/(cnt[2]/2)!/(cnt[3]/2)!/....../(cnt[26]/2)!   (cnt[i]!=0)
但是这样早就超过了long long,由于求的是模余系下后的结果,所以除运算可以用乘法逆元来解决。
乘法逆元的方法有两种:
扩欧和费马小定理(注意题目中的1000000007是一个质数)
以下用的是费马小定理的方法:
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
const LL maxn=2000+5 ,mod=1000000007 ;
char s[maxn];
LL f[maxn],cnt[30],r[maxn];
LL power_mod(LL a,LL b,LL t){
	LL ans=1;
	for(a%=t;b;a=a*a%t,b>>=1)
		if(b&1)ans=ans*a%t;
	return ans;
}
int main(){
	LL i,j,n,t=0,odd=0,ans;
	scanf("%s",s+1);
	n=strlen(s+1);
	for(i=1;i<=n;i++)
		cnt[s[i]-'a'+1]++;
	for(i=1;i<=26;i++){
		if(cnt[i]&1)odd++;
		cnt[i]>>=1;
	}
	t=n>>1;
	if(odd>1){
		cout<<"0";return 0;
	}
	f[0]=1;
	for(i=1;i<=t;i++)f[i]=f[i-1]*i%mod;
	for(i=1;i<=t;i++)
			r[i]=power_mod(f[i],mod-2,mod);
	ans=f[t];
	for(i=1;i<=26;i++)
		if(cnt[i])ans=ans*r[cnt[i]]%mod;
	cout<<ans%mod<<endl;
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值