字符串系列——EXKMP

版权声明:233 https://blog.csdn.net/gmh77/article/details/87932699

各种字符串算法

Manacher:https://blog.csdn.net/gmh77/article/details/78005017
KMP、AC自动机、回文自动机:https://blog.csdn.net/gmh77/article/details/79249222
SAM:https://blog.csdn.net/gmh77/article/details/79965599
EXKMP:https://blog.csdn.net/gmh77/article/details/87932699
SA:不知什么时候才会

前言

我好菜啊现在才会
EXKMP跟KMP并没有多大关系,相比之下更像Manacher
信息量略大

例题

jzoj4876. 【NOIP2016提高A组集训第10场11.8】基因突变
Description
邪恶的707刚刚从白垩纪穿越回来,心中产生了一个念头:我要统治人类!
但是统治人类是很庞大且复杂的一个工程,707尝试了洗脑,催眠,以及武装镇压都没能成功地统治人类,于是她决定从科学上对人类的基因进行研究从而达到他的目的。
707获取了人类的基因信息并尝试对基因进行实验。他发现可以把人类的基因看做一个只包含小写字母的字符串,并定义从头开始任意长度的基因为“源头基因”人类身上与源头基因完全匹配的片段越多,这个人就越容易被控制。于是707就开始了他邪恶的计划……
作为人类卫士的射手ZMiG自然不会让707得逞,他决定拯救人类,现在他拿到了其中一个人被改造后的基因,他想请你统计一下它的基因中究竟有多少基因片段是可以与源头基因相匹配的

Input
输入一个只包含小写字母的字符串S

Output
输出一个整数,代表可以与源头基因相匹配的基因片段数量。

Sample Input
【样例输入1】
aaba

【样例输入2】
niconiconi

Sample Output
【样例输出1】
6

【样例解释1】
这六个片段分别为(1,1),(1,2),(1,3),(1,4),(2,2),(4,4)

【样例输出2】
18

Data Constraint
对于30% 的数据,|S|<= 200
对于60% 的数据,|S|<= 2000
对于100%的数据,|S|<= 10^6

EXKMP

显然就是求每个后缀跟原串的最大前缀长度,然而并不能直接暴力匹配
于是就有了EXKMP


EXKMP是KMP的加强,用于解决最长匹配前缀之类的问题:
给出母串S和子串T,定义Next[i](i=1~|S|)表示最大满足S[i~i+Next[i]-1]=T[1~Next[i]]的Next[i]
(注意大小写尤其是Pascal选手

假设S=“aaaaaaaaaabaaa”,T=“aaaaaaaaaaa”
于是显然可以暴力求出Next[1]=10
但在求Next[2]时,还要从一开始比较吗?
因为Next[1]=10,所以得出S[1…10]=T[1…10]
也就是S[2…10]=T[2…10]
根据观察得出T[1…9]=T[2…10]
所以S[2…10]=T[1…9]
于是只需要从T[10]开始匹配,发现S[11]≠T[10],所以Next[2]=9


定义next[i](i=1~|T|)表示最大满足T[i~i+next[i]-1]=T[1~next[i]]的next[i],求法和Next类似
设先前最远到达的地方为p,对应的位置为a(跟Manacher类似)
则很显然S[a…p]=T[1…p-a+1]
那么S[i…p]=T[i-a+1…p-a+1]
所以设j=next[i-a+1],就有两种情况:
①i+j-1<p
那么S[i+j]一定不等于T[j+1],不然就和定义矛盾
所以Next[i]=j
②i+j-1≥p
那么可以发现,S[i…p]和T[1…p-i+1]一定相等,但后面的就不得而知了
所以就从S[p+1]和T[p-i+2]开始匹配求出Next[i],并更新a和p

因为有完全不匹配的情况,所以每次做之前都先判断S[i]是否等于T[1],这样可以避免a>p这种meaningless的情况
还有当i>p时p-i+2≤0,所以直接从S[i]和T[1]开始匹配就可以了


还有一个问题,就是next的求法
可以发现如果a取1的话会出现一些奇♂妙的问题
j=next[i-a+1]=next[i-1+1]=next[i]
然而现在就是要算next[i]
我 算 我 自 己
所以把a设为2,从2开始搞就行了
当然next[1]=|T|


至于为什么要维护最大的p,其实和Manacher类似,大概就是扩大影响范围并使其单调
因为要求的是从S[i]开始的前缀,所以a的位置并不会影响到结果,只需维护最大的p

题解

裸题就不说了吧。。。

code

/*
	Name: jzoj4876 gene
	Copyright: 
	Author: gmh77
	Date: 18/02/19 18:52
	Description: exkmp
*/

#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;

char s[1000001];
int n,i,j,k,l,a,p;
int next[1000001];
char ch;
long long ans;

int main()
{
	freopen("gene.in","r",stdin);
	freopen("gene.out","w",stdout);
	
	ch=getchar();
	while (ch>='a' && ch<='z')
	{
		s[++n]=ch;
		ch=getchar();
	}
	
	next[1]=n;
	a=2;
	p=2;
	while (p<=n && s[p]==s[p-1])
	++p;
	--p;
	next[2]=p-1;
	
	fo(i,3,n)
	if (s[i]==s[1])
	{
		if (i>p)
		{
			j=i;
			while (j<=n && s[j]==s[j-i+1])
			++j;
			--j;
			
			a=i;
			p=j;
			next[i]=p-i+1;
			continue;
		}
		
		j=next[1+(i-a)];
		
		if (i+j-1<p)
		next[i]=j;
		else
		{
			j=p+1;
			while (j<=n && s[j]==s[j-i+1])
			++j;
			--j;
			
			if (j>p)
			a=i,p=j;
			next[i]=j-i+1;
		}
	}
	
	fo(i,1,n)
	ans+=next[i];
	printf("%lld\n",ans);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}

例题2

hdu6153 A Secret

Problem Description
Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a present,which have a big secret.SF is interested in this secret and ask VS how to get it.There are the things that VS tell:
Suffix(S2,i) = S2[i…len].Ni is the times that Suffix(S2,i) occurs in S1 and Li is the length of Suffix(S2,i).Then the secret is the sum of the product of Ni and Li.
Now SF wants you to help him find the secret.The answer may be very large, so the answer should mod 1000000007.

Input
Input contains multiple cases.
The first line contains an integer T,the number of cases.Then following T cases.
Each test case contains two lines.The first line contains a string S1.The second line contains a string S2.
1<=T<=10.1<=|S1|,|S2|<=1e6.S1 and S2 only consist of lowercase ,uppercase letter.

Output
For each test case,output a single line containing a integer,the answer of test case.
The answer may be very large, so the answer should mod 1e9+7.

Sample Input
2
aaaaa
aa
abababab
aba

Sample Output
13
19
Hint

case 2:
Suffix(S2,1) = “aba”,
Suffix(S2,2) = “ba”,
Suffix(S2,3) = “a”.
N1 = 3,
N2 = 3,
N3 = 4.
L1 = 3,
L2 = 2,
L3 = 1.
ans = (3*3+3*2+4*1)%1000000007.

题解

就是求 S 2 \sum{S2中每个后缀出现次数*长度}
后缀不好搞,把两个串反过来就行了
(好像hdu语言不能选C++,只能选G++?)

code

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define mod 1000000007
using namespace std;

char s[1000001];
char t[1000001];
int _next[1000001];
int Next[1000001];
long long sum[1000002];
int a,p,T,i,j,k,l,l1,l2;
char ch;
long long ans;

void swap(char &x,char &y)
{
	char z=x;
	x=y;
	y=z;
}

bool pd(char ch)
{
	return ch>='a' && ch<='z' || ch>='A' && ch<='Z';
}

int main()
{
	for (scanf("%d",&T);T;--T)
	{
		l1=0,l2=0;
		
		ch=getchar();
		while (!pd(ch))
		ch=getchar();
		while (pd(ch))
		{
			s[++l1]=ch;
			ch=getchar();
		}
		
		ch=getchar();
		while (!pd(ch))
		ch=getchar();
		while (pd(ch))
		{
			t[++l2]=ch;
			ch=getchar();
		}
		
		fd(i,l1/2,1) swap(s[i],s[l1-i+1]);
		fd(i,l2/2,1) swap(t[i],t[l2-i+1]);
		
		_next[1]=l2;
		a=2;
		p=2;
		while (p<=l2 && t[p]==t[p-1])
		++p;
		--p;
		_next[2]=p-1;
		
		fo(i,3,l2)
		if (t[i]==t[1])
		{
			if (i>p)
			{
				a=i;
				p=i;
				while (p<=l2 && t[p]==t[p-i+1])
				++p;
				--p;
				_next[i]=p-i+1;
				
				continue;
			}
			
			j=_next[i-a+1];
			if (i+j-1<p)
			_next[i]=j;
			else
			{
				j=p;
				while (j<=l2 && t[j]==t[j-i+1])
				++j;
				--j;
				_next[i]=j-i+1;
				
				if (j>p)
				a=i,p=j;
			}
		}
		else
		_next[i]=0;
		
//		------
		
		a=1;
		p=1;
		while (p<=l1 && s[p]==t[p])
		++p;
		--p;
		Next[1]=p;
		
		fo(i,2,l1)
		if (s[i]==t[1])
		{
			if (i>p)
			{
				a=i;
				p=i;
				while (p<=l1 && s[p]==t[p-i+1])
				++p;
				--p;
				Next[i]=p-i+1;
				
				continue;
			}
			
			j=_next[i-a+1];
			if (i+j-1<p)
			Next[i]=j;
			else
			{
				j=p;
				while (j<=l1 && s[j]==t[j-i+1])
				++j;
				--j;
				Next[i]=j-i+1;
				
				if (j>p)
				a=i,p=j;
			}
		}
		else
		Next[i]=0;
		
//		------
		
		memset(sum,0,sizeof(sum));
		ans=0;
		
		fo(i,1,l1)
		++sum[Next[i]];
		
		fd(i,l1,1)
		{
			sum[i]+=sum[i+1];
			ans=(ans+sum[i]*i)%mod;
		}
		
		printf("%lld\n",ans);
	}
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}

一些资料

并没有参考
https://www.cnblogs.com/dilthey/p/8620119.html
https://blog.csdn.net/dyx404514/article/details/41831947

猜你喜欢

转载自blog.csdn.net/gmh77/article/details/87932699
今日推荐