zoukankan      html  css  js  c++  java
  • hdu 5651 反省

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5651
    xiaoxin巨从小就喜欢字符串,六年级的时候他就知道了什么是回文串。这时,xiaoxin巨说到:如果一个字符串 SS 是回文串,那么该字符串从前往后看和从后往前看是一样一样的。

    六年级的暑假,xiaoxin很快就做完了暑假作业,然后到腾讯做起了实习生。这日,leader给了xiaoxin一个字符串,请xiaoxin帮忙写一个函数来生成所有可能的回文串,可以任意改变字符串的顺序但是不可以扔掉某个字符。并且leader告诉xiaoxin,每生成一个不一样的回文串就可以得到一颗西瓜糖。
    
    请你帮忙计算xiaoxin的leader最多需要买多少颗西瓜糖呢?
    
    输入描述
    多组测试数据。第一行包含一个整数 T(T≤20) 表示组数。每组测试数据给出一个只包含小写字母的字符串 S(1≤length(S)≤1,000)
    输出描述
    对于每组测试数据,输出一个数, 表示leader需要买的西瓜糖的个数,结果对 1,000,000,007取模。
    输入样例
    3
    aa
    aabb
    a
    输出样例
    1
    2
    1
    

    题解:首先,如果不止一个字符出现的次数为奇数,则结果为0。 否则,我们把每个字符出现次数除2,也就是考虑一半的情况。 那么结果就是这个可重复集合的排列数了。 设该集合有n个数,第i个数出现次数为ai,那么结果就是 fact(n)/fact(a1)/fact(a2)/…./fact(an)。
    分析:昨晚做的BC,太失败了,就这一题,很快就推出公式了,跟题解一模一样!但是因为这个阶乘吧,不好搞,自己很快敲了一个代码,交了一下WA(意料之中)。因为我知道除法取余操作那块会出错,但是我不知道怎么解决啊。于是又想去推别的公式,但是每个想的角度一般是固定的,我想的就是先全排列,再把重复的排列一个个除去。虽然我知道肯定还有别的排列方法,但是怎么想也想不出别的了。于是重新去解决阶乘除法的问题,我知道最后分母,肯定会被分子n!约去,就想着先把分母全部约去,再相乘,这样就可以了。于是就敲,样例都对,就是过不了,当时看着200多个人都A了,又开始怀疑自己的公式是不是错了?于是又开始找公式,到最后实在没心情做了,放弃了。。。。
    做完后,掉了很多分,打击太大了,感觉到很迷茫啊!而后看题解,思路是一样的,难道我约分的地方错了?然后搜题解,有个人跟我想的一模一样!!!看我他的,我知道了,我约分是找完最小公倍数后相除。但是我不是用的队列,而是循环继续去找,这样就会导致有的分母会约不掉。。。
    以上就是全过程,主要原因还是我心态的问题,还以自己公式推错了(现在想想,怎么会错呢!!)然后就是自己代码能力太弱,没想到用队列啊!还有就是自己见识太短了,这种阶乘除法的题目正解是用阶乘逆元去做,而自己根本就没见过。还有就是思考问题的角度,还有一个很简单的公式,但是每个人思考的角度是不同的自己没想出来也难怪。

    法一:
    fact(n)/fact(a1)/fact(a2)/…./fact(an)。先分母全部约掉。

    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const int N=505;
    const int mod=1e9+7;
    char s[1001];
    int cnt[26],b[505];
    int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
    int main()
    {
        //freopen("f.txt","r",stdin);
        int T;scanf("%d",&T);
        while(T--){
            scanf("%s",s);
            int len=strlen(s);
            memset(cnt,0,sizeof(cnt));
            for(int i=0;i<len;i++)cnt[s[i]-'a']++;
            int tot=0;
            for(int i=0;i<26;i++){
                if(cnt[i]&1)tot++;
                cnt[i]>>=1;
            }
            if(tot>1){printf("0
    ");continue;}
            queue<int>q;
            for(int i=0;i<26;i++){
                if(cnt[i]){
                    for(int j=2;j<=cnt[i];j++)q.push(j);
                }
            }
            int n=len/2;
            for(int i=1;i<=n;i++)b[i]=i;
            while(!q.empty()){
                int t=q.front();q.pop();
                for(int i=2;i<=n;i++){
                    if(b[i]%t==0){
                        b[i]/=t;break;
                    }
                    int y=gcd(b[i],t);
                    if(y>1){
                        b[i]/=y;
                        t/=y;
                        if(t>1)q.push(t);
                        break;
                    }
                }
            }
            int ans=1;
            for(int i=1;i<=n;i++){
                ans=(ll)ans*b[i]%mod;
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    法二:
    每次从剩余的位置中选择需要的字母个数,自己需要的就是这个想法啊!

    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int mod=1e9+7;
    const int N=505;
    int n;
    char s[N];
    int a[27];
    ll f[N][N];
    void init()
    {
        for(int i=0;i<N;i++){
            f[i][0]=1;
            for(int j=1;j<=i;j++)f[i][j]=(f[i-1][j-1]+f[i-1][j])%mod;
        }
    }
    int main()
    {
    
       init();
        int T;scanf("%d",&T);
        while(T--){
            scanf("%s",s);
            int n=strlen(s);
            memset(a,0,sizeof(a));
            for(int i=0;i<n;i++){
                a[s[i]-'a']++;
            }
            int cnt=0;
            for(int i=0;i<26;i++){
                if(a[i]&1)cnt++,a[i]--;
            }
            if(cnt>1){
                printf("0
    ");continue;
            }
            int m=n/2;
            ll ans=1;
            for(int i=0;i<26;i++){
                ans=ans*f[m][a[i]>>1]%mod;
                m-=(a[i]>>1);
            }
            printf("%d
    ",(int)(ans));
        }
        return 0;
    }

    法三:
    逆元:a^(p-1) = 1(mod p)p为素数 于是 a*a^(p-2) = 1(mod p)所以a^(p-2)替代1/a.
    看到还有人的代码两行递推就可以,先放放,以后再看看(这几天太忙了)

    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int mod=1e9+7;
    const int N=505;
    char s[N];
    int a[27];
    int f[N];
    int fpow(int a,int p)
    {
        int res=1;
        for(;p;p>>=1){
            if(p&1)res=(ll)res*a%mod;
            a=(ll)a*a%mod;
        }
        return res;
    }
    int main()
    {
        f[1]=1;for(int i=2;i<N;i++)f[i]=(ll)f[i-1]*i%mod;
        int T;scanf("%d",&T);
        while(T--){
            scanf("%s",s);
            int n=strlen(s);
            memset(a,0,sizeof(a));
            for(int i=0;i<n;i++){
                a[s[i]-'a']++;
            }
            int cnt=0;
            for(int i=0;i<26;i++){
                if(a[i]&1)cnt++;
                a[i]>>=1;
            }
            if(cnt>1){
                printf("0
    ");continue;
            }
            int m=n/2;
            int ans=f[m];
            for(int i=0;i<26;i++){
                if(a[i])ans=(ll)ans*fpow(f[a[i]],mod-2)%mod;
            }
            printf("%d
    ",(int)(ans));
        }
        return 0;
    }

    法四:
    还有种最暴力,把所有的阶乘乘起来,最后再取模就好呗!(我咋没想到呢QAQ)
    另外,想到了也不一定敲得出来,自己JAVA还学得有点水啊

    import java.math.BigInteger;
    import java.util.Scanner;
    
    public class Main{
        static public void main(String[] args){
            BigInteger []fac=new BigInteger[505];
            fac[0]=new BigInteger("0");
            fac[1]=new BigInteger("1");
            for(int i=2;i<505;i++){
                fac[i]=fac[i-1].multiply(BigInteger.valueOf(i));
            }
            BigInteger mod=new BigInteger("1000000007");
            Scanner in=new Scanner(System.in);
            int []cnt=new int[26];
            int n;
            n=in.nextInt();
            while(n--!=0){
                String str;
                str=in.next();
                for(int i=0;i<26;i++)cnt[i]=0;
                for(int i=0;i<str.length();i++){
                    cnt[str.charAt(i)-'a']++;
                }
                int flag=0;
                for(int i=0;i<26;i++){
                    if((cnt[i]&1)==1)flag++;
                    cnt[i]>>=1;
                }
                if(flag>1){
                    System.out.println("0");
                    continue;
                }
                if(str.length()==1){
                    System.out.println("1");continue;
                }
                BigInteger ans=fac[str.length()/2];
                BigInteger sum=new BigInteger("1");
                for(int i=0;i<26;i++){
                    if(cnt[i]!=0){
                        sum=sum.multiply(fac[cnt[i]]);
                    }
                }
                System.out.println(ans.divide(sum).mod(mod));
            }
        }
    }
  • 相关阅读:
    布局重用 include merge ViewStub
    AS 常用插件 MD
    AS 2.0新功能 Instant Run
    AS .ignore插件 忽略文件
    AS Gradle构建工具与Android plugin插件【大全】
    如何开通www国际域名个人网站
    倒计时实现方案总结 Timer Handler
    AS 进行单元测试
    RxJava 设计理念 观察者模式 Observable lambdas MD
    retrofit okhttp RxJava bk Gson Lambda 综合示例【配置】
  • 原文地址:https://www.cnblogs.com/01world/p/5651243.html
Copyright © 2011-2022 走看看