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;
    }
}



原文地址:https://www.cnblogs.com/cutemush/p/12295936.html