KMP练习题

字符串 专栏收录该内容
5 篇文章 0 订阅

这里放一些KMP的题解。

caioj1457

【题意】
我们定义两个字符串 a a a b b b$的乘法: a ∗ b a*b ab ,就是把它们连接起来。比如: a a a = “ a b c abc abc” , b b b= " d e f " def" def" ,那么 a ∗ * b = “abcdef”.
由此推广,字符串的幂运算: a 0 a^0 a0 = “” (空字符串)
a ( n + 1 ) = a ∗ ( a n ) a^{(n+1)} = a*(a^n) a(n+1)=a(an).
给一个字符串 s s s,假设存在 a n = s a^n=s an=s,求 n n n的最大值。
【输入格式】
多组数据,每组数据一行字符串,长度在 1 1 1 1000000 1000000 1000000之间。句号"."表示结束。
【输出格式】
每组数据一行,一个整数,输出最大的 n n n。就是 s s s = a n a^n an a a a s s s的一个子串.
【样例输入】
abcd
aaaa
ababab
.
【样例输出】
1
4
3

思路

记得KMP的定义吗?

n e x t [ i ] next[i] next[i]表示"前缀子串"与“ i i i结尾的非前缀子串”的最长匹配长度。

A [ 1 ∼ n e x t [ i ] ] = A [ i − n e x t [ i ] + 1 ∼ i ] A[1\sim next[i]]=A[i-next[i]+1\sim i] A[1next[i]]=A[inext[i]+1i]

根据这个性质, A [ 1 ∼ n e x t [ n ] ] = A [ n − n e x t [ n ] + 1 ∼ n ] A[1\sim next[n]]=A[n-next[n]+1\sim n] A[1next[n]]=A[nnext[n]+1n]

n − n e x t [ n ] &lt; n e x t [ n ] n-next[n]&lt;next[n] nnext[n]<next[n]
w = n − n e x t [ n ] w=n-next[n] w=nnext[n] A [ 1 ∼ w ] = A [ w + 1 ∼ 2 ∗ w ] A[1\sim w]=A[w+1\sim 2*w] A[1w]=A[w+12w]

自己找规律就好了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=1e6+10;
int nxt[N];char s[N];
int main()
{
	while(scanf("%s",s+1)!=EOF)
	{
		if(s[1]=='.')break;
		int n=strlen(s+1);
		nxt[1]=0;
		for(int i=2,j=0;i<=n;i++)
		{
			while(j&&s[j+1]!=s[i])j=nxt[j];
			if(s[j+1]==s[i])++j;
			nxt[i]=j;
		}
		printf("%d\n",n%(n-nxt[n])==0?n/(n-nxt[n]):1);
	}
	return 0;
}

caioj1458

【题意】
给一个字符串,如果在前 i 位置处满足连续循环A^K(A:单位循环段,K:循环个数),
则输出i和K(仅输出K>1的情况,按i的递增顺序)
【输入格式】
一行一个字符串。字符串的长度为N (2 <= N <= 1 000 000)
【输出格式】
若干行的i和k。
【样例1输入】
aaa
【样例1输出】
2 2
3 3
【样例2输入】
aabaabaabaab
【样例2输出】
2 2
6 2
9 3
12 4

思路

和上一题一毛一样。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
const int N=1e6+10;
void qw(int x)
{
    if(x<0)x=-x,putchar('-');
    if(x/10)qw(x/10);
    putchar(x%10+48);
}
char s[N];int nxt[N];
int main()
{
    scanf("%s",s+1);
    int n=strlen(s+1);
    nxt[1]=0;
    for(int i=2,j=0;i<=n;i++)
    {
        while(j&&s[j+1]!=s[i])j=nxt[j];
        if(s[j+1]==s[i])++j;nxt[i]=j;
        if(nxt[i])
        {
            bool bk=i%(i-nxt[i]);
            if(!bk)qw(i),putchar(' '),qw(i/(i-nxt[i])),puts("");
        }
    }
    return 0;
}

caioj1459

【题意】
给出一个字符串S. 1 <= S的长度 <= 400000.
找出所有S的前缀等于后缀的情况。按长度递增输出长度。相互之间用空格隔开。
【输入格式】
多组数据。每组数据一行输入S字符串(一定要使用 while( scanf("%s")!=EOF ) )
【输出格式】
每组数据一行。从小到大输出S的前缀等于后缀的长度。
【样例输入】
ababcababababcabab
aaaaa
【样例输出】
2 4 9 18
1 2 3 4 5
样例1解释
ababcababababcabab
ab
ab:2
abab
abab:4
ababcabab
ababcabab:9(正中间隔开)
ababcababababcabab
ababcababababcabab:18(全部)

样例2解释:
aaaaa
a
a:1
aa
aa:2
aaa
aaa:3
aaaa
aaaa:4
aaaaa
aaaaa:5

思路

还记得引理吗?

n e x t [ n ] next[n] next[n]的候选项都来自于 n e x t [ n e x t [ n ] ] ⋯ ⋯ next[next[n]]\cdots\cdots next[next[n]],倒序输出所有 n e x t [ n ] next[n] next[n]的候选项即可。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
const int N=1e6+10;
void qw(int x)
{
    if(x<0)x=-x,putchar('-');
    if(x/10)qw(x/10);
    putchar(x%10+48);
}
char s[N];int nxt[N],ans[N],len;
int main()
{
    //freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    while(scanf("%s",s+1)!=EOF)
    {
        int n=strlen(s+1);
        nxt[1]=0;
        for(int i=2,j=0;i<=n;i++)
        {
            while(j&&s[j+1]!=s[i])j=nxt[j];
            if(s[j+1]==s[i])++j;nxt[i]=j;
        }
        ans[1]=n;len=1;
        int x=nxt[n];
        while(nxt[x])ans[++len]=x,x=nxt[x];
        if(x)ans[++len]=x;
        for(int i=len;i>=1;i--)qw(ans[i]),putchar(' ');
        puts("");
    }
    return 0;
}

caioj1460

题目描述】
给出两个字符串sa和sb,求出sa能在sb中匹配的最大次数。
【输入格式】
第一行一个整数n,表示下来有n组数据。
下来每组数据有两行,分别是字符串sa和字符串sb,都为大写字母。 1 ≤ |sa|≤1000000,|sa| ≤ |sb| ≤ 1000000.
|sa|,|sb|表示字符串sa和sb的长度。
【输出格式】
每组数据输出一个整数,表示最大的匹配次数。
【样例输入】
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
【样例输出】
1
3
0

思路

和模版没啥区别

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int N=1e6+10;
const int M=1e6+10;
int nxt[N],n,m;//next居然是系统函数 
char a[N],b[M];
void kmp()
{
    nxt[1]=0;int ans=0;
    for(int i=2,j=0;i<=n;i++)
    {
        while(j&&a[i]!=a[j+1])j=nxt[j];//j++
        if(a[i]==a[j+1])++j;nxt[i]=j;
    }
    for(int i=1,j=0;i<=m;i++)
    {
        while(j&&b[i]!=a[j+1])j=nxt[j];
        if(a[j+1]==b[i])++j;
        if(j==n){++ans;j=nxt[j];}
    }
    printf("%d\n",ans);
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%s%s",a+1,b+1);
        n=strlen(a+1),m=strlen(b+1);
        kmp();
    }
    return 0;
}
  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值