zoukankan      html  css  js  c++  java
  • Pku3461 oulipo

    题意:

    查找一个字符串在另一个字符串中出现的次数.

    Sample Input
    3
    BAPC
    BAPC
    AZA
    AZAZAZA
    VERDI
    AVERDXIVYERDIAN
    Sample Output
    1
    3
    0

    Sol:典型的字符hash.

    Std1:取mod

    #include<bits/stdc++.h>
    #define maxn 1e6+10 
    using namespace std;
    const int mod=19260817;
    long long  p[1000001],sum[1000001],num;
    int n;
    int b=193;
    char c1[100001],c2[1000001];
    
    int main()
    {
        p[0]=1;
        for(int i=1;i<=maxn;i++)// 求出b^i 
             p[i]=p[i-1]*b%mod;
        cin>>n;
        while(n--)
        {
            int ans=0;
            num=0;
            sum[0]=0;
            scanf("%s %s",c1+1,c2+1);
            //查找c1串在c2中出现的次数 
            int len1=strlen(c1+1);
            int len2=strlen(c2+1);
            for(int i=1;i<=len1;i++)//计算c1串的hash值 
                 num=((num*b%mod)+(c1[i]-'A'+1))%mod;
            for(int i=1;i<=len2;i++)//将c2串的前若干位的值计算出来 
                 sum[i]=((sum[i-1]*b%mod)+(c2[i]-'A'+1))%mod;
            for(int i=0;i<=len2-len1;i++)
                 if(num==((sum[i+len1]-sum[i]*p[len1]%mod)+mod)%mod)
                   ans++;
            printf("%d
    ",ans);
        }
        return 0;
    }

    Std2:用无符号整数计算hash值,用自然溢出求掉求模运算

    #include<bits/stdc++.h>
    #define maxn 1e6+10 
    using namespace std;
    typedef unsigned long long ull;
    ull p[1000001],sum[1000001],Num;
    int n;
    int b=193;
    char c1[100001],c2[1000001];
    void read(int &x) 
    {
    	char ch; bool ok;
    	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
    }
    int main()
    {
    	
    	p[0]=1;
    	for(int i=1;i<=maxn;i++)
    	     p[i]=p[i-1]*b;
    	read(n);
    	while(n--)
    	{
    		int ans=0;
    		Num=0;
    		sum[0]=0;
    		cin>>c1+1>>c2+1;
    		int len1=strlen(c1+1);
    		int len2=strlen(c2+1);
    		for(int i=1;i<=len1;i++)
    		    Num=Num*b+ull(c1[i]-'A'+1);
    		for(int i=1;i<=len2;i++)
    		    sum[i]=sum[i-1]*b+ull(c2[i]-'A'+1);
    		for(int i=0;i<=len2-len1;i++)
    		     if(Num==sum[i+len1]-sum[i]*p[len1])
    		         ans++;
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    Sol3:Kmp

    /*
    本题匹配出来的子串是可以有交集的.
    另一个变形题来自Hdu2087 剪花布条
    其要求子串,不能有交集
    */

    #include<cstdio>
    #include<cstring>
    using namespace std;
    int pre[10010],t,n,m,ans;
    char a[10010],b[1000010];
     
    void get_next()
    {
        int j=0;
        for (int i=1;i<n;i++)
        {
            while (j>0&&a[j+1]!=a[i+1]) j=pre[j];
            if (a[j+1]==a[i+1]) j++;
            pre[i+1]=j;
        }
    }
     
    void get_ans()
    {
        ans=0;
        int j=0;
        //认为母串的第i位已与子串的第j位匹配成功
        //接下来试图找子串的第j+1位与母串的第i+1位匹配
        for (int i=0;i<m;i++)
        {
            while (j>0&&a[j+1]!=b[i+1])
                j=pre[j];
            if (a[j+1]==b[i+1])
            //如果找成功则j++,注意此时i的值还没有变化
            //等到下一轮时即i++后称之为i',i'与j是匹配成功(在当前这一轮保证的)
            //则如果我们希望继续匹配下去,就有两种情况
            //一种是找出来的子串有交集,则取j=pre[j]
            //另一种是没有交集,则说明i'这个字符是不能被匹配到的
            //即我们不能让子串中任一个字符与之匹配,于是j=0
                j++;
            if (j==n)
            {
                j=pre[j];
            
                ans++;
            }
        }
        printf("%d ",ans);
    }
     
    int main(){
        scanf("%d",&t);
        while (t--){
            memset(pre,0,sizeof(pre));
            scanf("%s",a+1);
            n=strlen(a+1);
            scanf("%s",b+1);
            m=strlen(b+1);
            get_next();
            get_ans();
        }
        return 0;
    }
     

    Sol4:扩展Kmp

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    using namespace std;
      
    char S[11000005], T[11000005];
    int Slen, Tlen, nxt[11000005], lcp[11000005];
      
    int main()
    {
    int run1;
    cin>>run1;
    for (int rr=1;rr<=run1;rr++)
    {
        scanf("%s", T);//子串 
        scanf("%s", S);//母串 
        
            Slen = strlen(S), Tlen = strlen(T);
            nxt[0] = Tlen;
            memset(lcp,0,sizeof(lcp));
            for(register int i = 1, a = 0, p = 0; i < Tlen; i++)
            {
                if(i + nxt[i - a] >= p)
                {
                    if(i > p)
                    //有可能输入的两个字符串完全不相等,那p的值就要等于i
                    //也就是说始终间将第一个字符串的第i个字符与第二个字符串的第一个字符开始比
                    //p代表从前比较时,最靠左的失效位置, 在我们的讨论中一直认为i<p,所以当i>p时,p=i
                    //于是从从前的失效位置开始比较,即从t[p]开始与t[p-i]开始比较,
                        p = i;
                    while(p < Tlen && T[p] == T[p - i]) p++;
                    nxt[i] = p - i, a = i;
                }
                else nxt[i] = nxt[i - a];
            }
            for(register int i = 0, a = 0, p = 0; i < Slen; i++)
    		{
                if(i + nxt[i - a] >= p)
    			{
                    if(i > p) p = i;
                    while(p < Slen && S[p] == T[p - i]) p++;
                    lcp[i] = p - i, a = i;
                }
                else lcp[i] = nxt[i - a];
            }
            int ans=0;
            for(register int i = 0; i <= Slen - 1; i++) 
                if (lcp[i]==Tlen)
                     ans++;
            printf("%d
    ", ans);
        }
    
        return 0;
    }
    

      

    类似的习题有Poj2406 Power String,此题还可以用Kmp算法来做

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    using namespace std;
    typedef unsigned long long LL;
    const LL base=131;
    const int N=1000010;
    int n;
    LL power[N],sum[N];
    bool check(LL v,int k)  //判断s[1]~s[k]是否是循环节
    {
        for(register int i=1;i<=n;i+=k) //一路推过去每k位都应该值等于v 
    	{
            if(v!=sum[i+k-1]-sum[i-1]*power[k]) 
    		    return 0;
        }
        return 1;
    }
    int main()
    {
        power[0]=1;
        for(register int i=1;i<=N-10;++i) //hash准备工作
            power[i]=power[i-1]*base;
        char s[N];
        while(scanf("%s",s+1))
    	{
            if(s[1]=='.')break;
            n=strlen(s+1);
            sum[0]=0;
            for(register int i=1;i<=n;++i) 
    		     sum[i]=sum[i-1]*base+LL(s[i]);
            for(register int i=1;i<=n;++i)
    		{
                if(n%i) //循环节长度i,应为n的约数 
    			   continue;
                LL expect=sum[i];
                if(check(expect,i))
    			{
                    printf("%d
    ",n/i);
                    break;
                }   
            }
        }
        return 0;
    }
    

      

     HDU 2087 剪花布条
    一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
    输入格式

    输入数据为多组数据,读取到 # 字符时结束。每组数据仅有一行,为由空格分开的花布条和小饰条。花布条和小饰条都是用可见 ASCII 字符表示的,不会超过1000 个字符。

    注意:这个 # 应为单个字符。若某字符串开头有 #,不意味着读入结束!
    输出格式

    对于每组数据,输出一行一个整数,表示能从花纹布中剪出的最多小饰条个数。
    样例
    样例输入

    abcde a3
    aaaaaa aa
    #

    样例输出

    0
    3

    #include <bits/stdc++.h>
    using namespace std;
    int ans, len1, len2;
    int next[1000001];
    char s1[1000001];
    char s2[1000001];
    void gnext() {
        next[1] = 0;
        for (int i = 1, j = 0; i < len2; i++) {
            while (j && s2[i + 1] != s2[j + 1]) j = next[j];
            if (s2[i + 1] == s2[j + 1])
                j++;
            next[i + 1] = j;
        }
    }
    void KMP() {
        for (int i = 0, j = 0; i < len1; i++) {
            while (0 < j && s1[i + 1] != s2[j + 1]) j = next[j];
            if (s1[i + 1] == s2[j + 1])
                j++;
            if (j == len2) {
                ans++;
                j = 0;
            }
        }
    }
    int main() {
        while (cin >> s1 + 1) {
            ans = 0;
            if (s1[1] == '#' && strlen(s1 + 1) == 1)
                return 0;
            cin >> s2 + 1;
            len1 = strlen(s1 + 1);
            len2 = strlen(s2 + 1);
            gnext();
            KMP();
            cout << ans << endl;
        }
    }



  • 相关阅读:
    阻止用户复制页面上的文字的几种方法
    js设置聊天信息停留在最底部
    js动态删除表格中的某一行
    XmlSerializer vs DataContractSerializer: Serialization in Wcf
    WCF Service Binding Explained
    Visual Studio设置远程调试
    Could not download the Silverlight application
    .NET 中的三种接口实现方式
    化零为整WCF(9) 序列化(DataContractSerializer, XmlSerializer, DataContractJsonSerializer, SoapFormatter, BinaryFormatter)
    化零为整WCF(14) 事务(Transaction)
  • 原文地址:https://www.cnblogs.com/cutemush/p/12295936.html
Copyright © 2011-2022 走看看