KMP相关

题目链接:点这里
A、POJ 3461–Oulipo
B、HDU 2087–剪花布条
C、POJ 2406–Power Strings
D、HDU 3746–Cyclic Nacklace
E、HDU 3336–Count the string
F、POJ 1961–Period
G、POJ 2752–Seek the Name, Seek the Fame
H、HDU 1711–Number Sequence

A. Oulipo

题目链接:poj 3461
题目大意是说求字符串W在字符串T中出现的次数。经典的KMP匹配问题。只需要匹配成功后记录,模式串在回溯回去。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
const int maxn=1e6+10;
using namespace std;
string str1,str2;
int Next[maxn];
void GetNext()
{
	mem(Next,0);
	Next[0]=-1;
	int j=0,k=-1;
	int len=str1.size();
	while(j<len)
	{
		if(k==-1||str1[j]==str1[k])
		{
			j++;
			k++;
			if(str1[j]!=str1[k])
				Next[j]=k;
			else
				Next[j]=Next[k];
		}
		else
		{
			k=Next[k;
		}
	}
}
int KMP()
{
	int i=0,j=0,ans=0;
	int lenp=str1.size(),lens=str2.size();
	while(i<lenp&&j<lens)
	{
		if(i==-1||str1[i]==str2[j])
		{
			i++;
			j++;
		}
		else
		{
			i=Next[i];
		}
		if(i==lenp)            //当匹配成功后ans++,i值再回溯
		{
			ans++;
			i=Next[i];
		}
	}
	return ans;
}
int main()
{
	IOS();
	int t;
	cin>>t;
	while(t--)
	{
		cin>>str1>>str2;   //分清楚哪个是模式串(str1),哪个是文本串(str2),别弄混淆
		GetNext();
		cout<<KMP()<<endl;
	}
	return 0;
}

B. 剪花布条

题目链接:hdu 2087
题意同样是求模式串在文本串中出现的次数。与第一题不同的是当匹配成功时,模式串要回退到0的位置。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
const int maxn=1e6+10;
using namespace std;
string str1,str2;
int Next[maxn];
void GetNext()
{
	mem(Next,0);
	Next[0]=-1;
	int j=0,k=-1;
	int len=str2.size();
	while(j<len)
	{
		if(k==-1||str2[j]==str2[k])
		{
			j++;
			k++;
			if(str2[j]!=str2[k])
				Next[j]=k;
			else
				Next[j]=Next[k];
		}
		else
		{
			k=Next[k];
		}
	}
}
int KMP()
{
	int i=0,j=0,ans=0;
	int len1=str1.size(),len2=str2.size();
	while(i<len1&&j<len2)
	{
		if(j==-1||str1[i]==str2[j])
		{
			i++;
			j++;
		}
		else
		{
			j=Next[j];
		}
		if(j==len2)
		{
			ans++;
			j=0;
		}
	}
	return ans;
}
int main()
{
	IOS();
	while(cin>>str1)
	{
		if(str1=="#")
			break;
		cin>>str2;
		GetNext();
		cout<<KMP()<<endl;
	}
	return 0;
}

C. Power Strings

题目链接:POJ 2406
题目是问,在一个字符串S中,最多有几个相同的子串连接而成。例如:字符串ababab,它最多是由3个子串ab连接而成。
此题主要使用KMP算法的定义。
对于一个长度为len的字符串S,若Next[len]=k,则S[0 ~ k-1]==S[len-k ~ len-1]。k是字符串S前缀后缀的最大匹配长度。当len-next[len]是len的约数时,则S[0 ~ (len-Next[len]-1)]一定是字符串S的最大重复字串,长度为len-Next[len],并且循环次数为 len/(len-Next[len])
例如:字符串S:ababab
在这里插入图片描述
Next[len]=4,那么在字符串S中最长的匹配长度为4,S[0 ~ 3]==S[2 ~ 5]。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
const int maxn=1e6+10;
using namespace std;
string str1;
int Next[maxn];
void GetNext()
{
	mem(Next,0);
	Next[0]=-1;
	int j=0,k=-1;
	int len=str1.size();
	while(j<len)  //需要求出Next[len],使用j<len
	{
		if(k==-1||str1[j]==str1[k])
		{
			j++;
			k++;
			if(str1[j]!=str1[k])
				Next[j]=k;
			else
				Next[j]=Next[k];
		}
		else
		{
			k=Next[k];
		}
	}
}
int main()
{
	IOS();
	while(cin>>str1)
	{
		if(str1==".")
			break;
		GetNext();
		int len=str1.size();
		if(len%(len-Next[len])==0)
			cout<<len/(len-Next[len])<<endl;
		else
			cout<<1<<endl;   //如果没有,那就是它自身
	}
	return 0;
}

D. Cyclic Nacklace

题目链接:HDU 3746
题意:对于一个字符串,在串头或者串尾最少添加多少个字符,使得这个字符串至少有两个循环节。
例如 :abca,需要添加两个字符bc,然后abcabc就会有两个循环节(abc)。
这题需要对Next数组详细观察了解。始终要牢记Next[i]数组就是在i之前最大匹配的前缀后缀长度。
首先考虑两种特殊情况:
(1)至少需要在添加等长的字符串长度。如abcde。这时只能在添加与之相等长度的字符串,才能保证至少有两个循环。
(2)不需要再添加字符,原字符已经有了至少两个循环。例如abab。
对于第一种情况,可以观察求得的Next数组,当Next[length]==0时,只能在添加等长字符串长度;
第二种情况,其实就是上面第三题中提到的KMP算法的一些定义。当字符串是有若干个子串循环重复得来,那么就不需要在添加。
当不满足以上情况时,再观察Next数组,根据Next的定义,len-Next[len]一定是字符串中最小的循环长度。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
const int maxn=1e5+10;
using namespace std;
string str1;
int Next[maxn];
void GetNext()
{
	mem(Next,0);
	Next[0]=-1;
	int j=0,k=-1;
	int len=str1.size();
	while(j<len)
	{
		if(k==-1||str1[j]==str1[k])
		{
			j++;
			k++;
			if(str1[j]!=str1[k])
				Next[j]=k;
			else
				Next[j]=Next[k];
		}
		else
		{
			k=Next[k];
		}
	}
}
int main()
{
	IOS();
	int t;
	cin>>t;
	while(t--)
	{
		cin>>str1;
		GetNext();
		int len=str1.size();
		if(Next[len]==0)
			cout<<len<<endl;
		else
		{
			if(len%(len-Next[len])==0)
				cout<<0<<endl;
			else
			{
               int len1=len-Next[len];  //需要求最小的添加长度
               cout<<len1-len%len1<<endl;
			}
		}
	}
	return 0;
}

E. Count the string

题目链接:HDU 3336
题目大意是说,在一个字符串中,求它每一个前缀在串中出现的次数。例如字符串abab,它的所有前缀包括
a,ab,aba,abab,在串中分别出现2+2+1+1共6次。
此题一个简单的求法是循环遍历Next数组。对于长度为len的字符串,它一共会有len个前缀。然后在遍历Next数组,当Next[i]!=0,说明当前字符匹配的有前缀,所以次数+1。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
const int maxn=2e5+10;
const int mod=10007;
using namespace std;
string str1;
int Next[maxn];
void GetNext()
{
	mem(Next,0);
	Next[0]=-1;
	int j=0,k=-1;
	int len=str1.size();
	while(j<len)
	{
		if(k==-1||str1[j]==str1[k])
		{
			j++;
			k++;
			Next[j]=k;
		}
		else
		{
			k=Next[k];
		}
	}
}
int main()
{
	IOS();
	int t,len,sum;
	cin>>t;
	while(t--)
	{
		cin>>len>>str1;
		GetNext();
		sum=len%mod;
		for(int i=1; i<=len; i++)
		{
			sum=(sum+(Next[i]==0?0:1))%mod;
		}
		cout<<sum<<endl;
	}
	return 0;
}

F. Period

题目链接:POJ 1961
题意:对于一个字符串,求串首到第 i (1=<i<=len)字符共循环节出现了多少次。
例如:aabaabaabaab
到第2个字符 aa , a共出现了2次;
到第6个字符 aabaaab,aab共出现2次;
到第9个字符aabaabaab,aab共出现3次;
到第12个字符aabaabaabaab,aab共出现4次。
此题是第三题的一种变形。从求整个字符串的循环节出现的次数,到求对于每一个字符前,循环节出现的次数。
所以在第三题的基础上再一个循环判断即可。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
const int maxn=1e6+10;
using namespace std;
string str1;
int Next[maxn];
void GetNext()
{
	mem(Next,0);
	Next[0]=-1;
	int j=0,k=-1;
	int len=str1.size();
	while(j<len)
	{
		if(k==-1||str1[j]==str1[k])
		{
			j++;
			k++;
			Next[j]=k;
		}
		else
		{
			k=Next[k];
		}
	}
}
int main()
{
	IOS();
	int ca=1,n;
	while(cin>>n&&n)
	{
		cin>>str1;
		GetNext();
		printf("Test case #%d\n", ca++);
		for(int i=1; i<=str1.size(); i++)
		{
			if(i%(i-Next[i])==0&&Next[i])
				printf("%d %d\n", i, i / (i-Next[i]));
		}
		cout<<endl;
	}
	return 0;
}

G. Seek the Name, Seek the Fame

题目链接:POJ 2752
题意:对于一个字符串,求它存在的所有前缀后缀相等的长度。
例如:ababcab
前缀有:a,ab,aba,abab,ababc,ababca,ababcab
后缀有:b,ab,cab,bcab,abcab,babcab,ababcab
所以匹配的最大长度为,2,7.
因为匹配的是后缀,所以每一个满足条件的前缀,它的最后一个字符一定与原字符串中的最后一个字符相等。只需要不断的回溯Next数组,然后在比较最后一个字符即可。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
const int maxn=4e5+10;
using namespace std;
string str1;
int Next[maxn];
int Res[maxn];     //用Res数组来保存满足条件的子串长度
void GetNext()
{
	mem(Next,0);
	Next[0]=-1;
	int j=0,k=-1;
	int len=str1.size();
	while(j<len)
	{
		if(k==-1||str1[j]==str1[k])
		{
			j++;
			k++;
			Next[j]=k;
		}
		else
		{
			k=Next[k];
		}
	}
}
int main()
{
	IOS();
	int i,t,len;
	while(cin>>str1)
	{
		GetNext();
		len=str1.size();
	    i=0,t=Next[len-1];
	    while(t!=-1)
	    {
	    	if(str1[t]==str1[len-1])
	    	 Res[i++]=t+1;
	    	 t=Next[t];  //不断的回溯Next[t]的值
		}
		for(int j=i-1;j>=0;j--)  //注意需要倒着输出
		 cout<<Res[j]<<' ';
		 cout<<len<<endl;    //包括它自身
	}
	return 0;
}

H. Number Sequence

题目链接:HDU 1711
KMP模板题。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS() std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
using namespace std;
const int maxn=1e6+10;
int str1[maxn],str2[maxn];
int Next[maxn];
int m,n;
void GetNext()
{
    int j=0,k=-1;
    Next[0]=-1;
    while(j<m-1)
    {
        if(k==-1||str2[k]==str2[j])
        {
            k++;
            j++;
            Next[j]=k;
        }
        else
        {
            k=Next[k];
        }
    }
}
int KMP()
{
    GetNext();
    int i=0,j=0;
    while(i<n&&j<m)
    {
        if(j==-1||str1[i]==str2[j])
        {
            i++;
            j++;
        }
        else {
            j=Next[j];
        }
    }
    if(j==m)
        return i-j+1;
    else
        return -1;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        mem(Next,0);
        cin>>n>>m;
        for(int i=0; i<n; i++)
            cin>>str1[i];
        for(int j=0; j<m; j++)
            cin>>str2[j];
        cout<<KMP()<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值