【无标题】

一:KMP字符串匹配

题目描述

给出两个字符串 s_1s1​ 和 s_2s2​,若 s_1s1​ 的区间 [l, r][l,r] 子串与 s_2s2​ 完全相同,则称 s_2s2​ 在 s_1s1​ 中出现了,其出现位置为 ll。
现在请你求出 s_2s2​ 在 s_1s1​ 中所有出现的位置。

定义一个字符串 ss 的 border 为 ss 的一个非 ss 本身的子串 tt,满足 tt 既是 ss 的前缀,又是 ss 的后缀。
对于 s_2s2​,你还需要求出对于其每个前缀 s's′ 的最长 border t't′ 的长度。

输入格式

第一行为一个字符串,即为 s_1s1​。
第二行为一个字符串,即为 s_2s2​。

输出格式

首先输出若干行,每行一个整数,按从小到大的顺序输出 s_2s2​ 在 s_1s1​ 中出现的位置。
最后一行输出 |s_2|∣s2​∣ 个整数,第 ii 个整数表示 s_2s2​ 的长度为 ii 的前缀的最长 border 长度。

输入输出样例

输入 #1复制

ABABABC
ABA

输出 #1复制

1
3
0 0 1 

说明/提示

样例 1 解释

【无标题】_第1张图片

对于 s_2s2​ 长度为 33 的前缀 ABA,字符串 A 既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 11。

数据规模与约定

本题采用多测试点捆绑测试,共有 3 个子任务

  • Subtask 1(30 points):|s_1| \leq 15∣s1​∣≤15,|s_2| \leq 5∣s2​∣≤5。
  • Subtask 2(40 points):|s_1| \leq 10^4∣s1​∣≤104,|s_2| \leq 10^2∣s2​∣≤102。
  • Subtask 3(30 points):无特殊约定。

对于全部的测试点,保证 1 \leq |s_1|,|s_2| \leq 10^61≤∣s1​∣,∣s2​∣≤106,s_1, s_2s1​,s2​ 中均只含大写英文字母。

题解:

1:首先了解部分匹配值:

以ABCDABD为例。

"A"的前缀和后缀都为空集,共有元素的长度为0;

"AB"的前缀为[A],后缀为[B],共有元素的长度为0;

"ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

"ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

"ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

"ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

"ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

那么知道了部分匹配值,这有一个公式:移动位数 = 已匹配的字符数 - 对应的部分匹配值

它的代码如下:

  for(int i=2;i<=len2;i++)
   {
    while(i1&&str2[i1+1]!=str2[i])
    {
        i1=n[i1];
    }
    if(str2[i1+1]==str2[i])
    {
        i1++;
    }
    n[i]=i1;
   }

while(i1&&str2[i1+1]!=str2[i]):如果str2[i1+1]!=str2[i]那么这串连续的相等断了,就无法继承之前的情况。然后顺着next我们可以找到能够让我们继续匹配的值,但i1不能为0并且如果我们找到了str2[i1+1]==str2[i]的地方,那么就可以从这里开始继承

如果相等就比下一个,同时这也是计数+1

最后把算出的值给next

代码:

#include
using namespace std;
char str1[1000005],str2[1000005];
int len1,len2,i1;
int n[1000005];
int main()
{
    cin>>str1+1>>str2+1;
    len1=strlen(str1+1);
    len2=strlen(str2+1);
    for(int i=2;i<=len2;i++)//next数组处理
   {
    while(i1&&str2[i1+1]!=str2[i])//如果str2[i1+1]!=str2[i]那么这串连续的相等断了,所以无法继承之前的情况
    {
        i1=n[i1];//顺着next我们可以找到能够让我们继续匹配的值,但i1不能为0并且如果我们找到了str2[i1+1]==str2[i]的地方,那么就可以从这里开始继承
    }
    if(str2[i1+1]==str2[i])
    {
        i1++;//相等就比下一个,同时这也是计数+1
    }
    n[i]=i1;//把算出的值给next
   }
    i1=0;//初始化
    for(int i=1;i<=len1;i++)//和求next差不多
    {
        while(i1&&str2[i1+1]!=str1[i])
        {
            i1=n[i1];
        }
        if(str2[i1+1]==str1[i])
        {
            i1++;
        }
        if(i1==len2)
        {
            cout<

P4391 [BOI2009]Radio Transmission 无线传输

题目描述

给你一个字符串 �1s1​,它是由某个字符串 �2s2​ 不断自我连接形成的(保证至少重复 22 次)。但是字符串 �2s2​ 是不确定的,现在只想知道它的最短长度是多少。

输入格式

第一行一个整数 �L,表示给出字符串的长度。

第二行给出字符串 �1s1​ 的一个子串,全由小写字母组成。

输出格式

仅一行,表示 �2s2​ 的最短长度。

输入输出样例

输入 #1复制

8
cabcabca

输出 #1复制

3

说明/提示

样例输入输出 1 解释

对于样例,我们可以利用 abcabc 不断自我连接得到 abcabcabcabcabcabc,读入的 cabcabcacabcabca,是它的子串。

#include
using namespace std;
#define N 1000010
int n,nxt[N];
char s[1000010];
int next()
{
	int i,j;
	for(i=1,j=0;i=n) break;
		
		j=nxt[j-1];
		while(i>n;
	cin>>s;
	kmp();
}

你可能感兴趣的:(c++,算法,java)