NKOI 3681 回文

                                                           回文

时间限制 :  - MS    空间限制 :  65536 KB  评测说明 : 时限1000ms

问题描述:

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

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

    例如给出字符串“aabb”      

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

输入格式:

一行,一个字符串

输出格式:

一个整数,表示所求答案

样例输入:

aabb

样例输出:

2

提示:

1<=n<=2000
首先分析满足构成回文串的条件
1.N为奇数:串中只有一个字母出现次数为奇数,其余全为偶数;
2.N为偶数:串中所有字母出现的次数都为偶数
然后我们再分析在满足构成回文串的条件下计算方案的方法
事先统计出每个字母出现的次数,分别为cnt1,cnt2,......,cnt26
因为回文串左右对称,我们只需要讨论左边N/2个位置的情况即可。
令N=N/2, cnt1/=2 , cnt2/=2  ,......., cnt26/=2
对于字母'a',它可以从N个位置中选cnt1个位置出来放置
方案数为C[N][cnt1]
对于字母'b',它可以从N-cnt1个位置中选cnt2个位置出来放置,方案数为C[N-cnt1][cnt2]
以此类推,总方案数即为C的所有乘积对于1000000007取模
但是为了防止中间结果溢出,我们应该注意用乘法逆元求解
根据组合数公式:
化简得:((N!)/ (cnt1! * cnt2! *......*cnt26!) ) mod 1000000007
令T=cnt1! * cnt2! *......*cnt26!     根据(T * T-1) ≡ 1 (mod 100000007)
通过扩欧可求得T的乘法逆元T-1 
((N!)/ (cnt1! * cnt2! *......*cnt26!) ) mod 1000000007
=(N! * T-1)mod 100000007
=((N! mod 100000007)*(T-1 mod 100000007)) mod 100000007 
#include<iostream>   
#include<cstdio>   
#include<cstring> 
#define LL long long   
using namespace std; 
const int mod=1000000007; 
char s[2005]; 
LL cnt[30],fr[2005],r[2005]; //r数组记录的是乘法逆元
LL adv(LL a,LL b,LL &x,LL &y){   
    LL x0,y0,r;   
    if(b==0){x=1;y=0;return a;}   
    r=adv(b,a%b,x0,y0);   
    x=y0;y=x0-a/b*y0;   
    return r;   
}  //拓展欧几里德
int main(){ 
    LL i,j,n,odd=0,t=0,x,y; 
    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+=cnt[i]; 
    } 
    if(odd>1){puts("0");return 0;} //判断无法凑成回文的情况
    fr[0]=1; 
    for(i=1;i<=t;i++)fr[i]=fr[i-1]*i%mod; 
    LL ans=fr[t]; 
    for(i=1;i<=t;i++){ 
        LL gcd=adv(fr[i],mod,x,y); 
        if(gcd==1)r[i]=(x+mod)%mod; 
        else r[i]=-1; //若无乘法逆元,返回-1
    }
    for(i=1;i<=26;i++) 
        if(cnt[i])ans=ans*r[cnt[i]]%mod; 求最终答案
    cout<<ans%mod;
}

时间限制 : - MS   空间限制 : 65536 KB  评测说明 : 时限1000ms
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值