【拓展kmp】

本文深入探讨了两种字符串匹配算法:一种用于寻找两个字符串之间的特定形式的匹配子串,另一种则针对更为复杂的字符串翻转和拼接问题。通过具体的代码实现和步骤解释,展示了如何高效地解决这些挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

太久不写,比赛时都不敢直接敲了...

Kuala2011 G

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
char a[200000],b[200000],ch[200000];
int n,m,len,f[200000],t;
int main()
{
	freopen("input.txt","r",stdin);
//	freopen("output.txt","w",stdout);
	scanf("%d",&t);
	for (;t;t--) {
		scanf("%s",a+1);
		scanf("%s",b+1);
		n=strlen(a+1),m=strlen(b+1);
		len=0;
		for (int i=1;i<=m;i++) ch[++len]=b[i];
		ch[++len]='$';
		for (int i=1;i<=n;i++) ch[++len]=a[i];
		int j;
		f[1]=0,j=1;
		for (int i=2;i<=len;i++) {
			if (j+f[j]>=i) f[i]=min(f[i-j+1],j+f[j]-i);else f[i]=0;
			f[i]=max(f[i],0);
			for (;(f[i]>=0) && (i+f[i]<=len) && (ch[1+f[i]]==ch[i+f[i]]);) f[i]++;
			f[i]--;
			if (i+f[i]>j+f[j]) j=i;
		}
		int ans=1;
		//for (int i=1;i<=len;i++) cout<<f[i]<<' ';cout<<endl;
		for (int i=m+2;i<=len;i++)
			if (i+f[i]==len) ans++;
		printf("%d\n",ans);
	}
	return 0;
} 

定义函数R(X),表示把X这个字符串翻转:

R(‘ASD’)=’DSA’;

R(‘aaa’)=’aaa’;

再定义函数F(s,i,j),其中F的自变量s为字符串,i、j为0到N-1的整数,i<j:

f(s, i, j) = s[i + 1... j - 1] + r(s[j... n - 1]) + r(s[0... i]).

//”+”即为字符串首尾相接,这个字符串下标为0~n-1,s[i…j]表示从下标i到下标j的子串。

有两个字符串A,B,要求出i、j,满足F(A,i,j)=B;

如果没有这样的i,j,输出“-1 -1”;

如果有多组解,首先令i最大,然后令j最小。

考虑枚举i的位置,那么B的后半段和A的前半段可以确定i是否可行,然后B的开头就确定是i+1了,我们将B+A求一次拓展kmp,可知道在B的前1+f[i+1]中的k都是一个可能的j-1,

同时为了使A[n~k+1]==B[k+1~i],再对r(A)+B求一次拓展kmp,如果k+1+g[k+1]>=n-i的话,k就是可行的,因此这样就是在二维偏序中求极值,o(nlogn)的时间就可以解决

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
const int oo=1073741819;
using namespace std;
int f[2500000],g[2500000],n,m,m1;
int c[2500000],s[6000000],st[2500000];
char a[1500000],b[1500000];
bool cmp(int i,int j)
{
	return min(1+f[i+1+m+1],n-i)<min(1+f[j+1+m+1],n-j);
}
void change(int x,int w)
{
	for (x+=m1;x;x>>=1) s[x]=min(s[x],w);
}
int ask(int l,int r)
{
	int sum=oo;
	l+=m1-1,r+=m1+1;
	for (;!((l^r)==1);l>>=1,r>>=1) {
		if ((l&1)==0) sum=min(sum,s[l+1]);
		if ((r&1)==1) sum=min(sum,s[r-1]);
	}
	return sum;
}
int main()
{
	freopen("trans.in","r",stdin);
	freopen("trans.out","w",stdout);
	//scanf("%s",a+1);
	//scanf("%s",b+1);
	gets(a+1);
	gets(b+1);
	n=strlen(a+1),m=strlen(b+1);
	if (n!=m) {
		printf("-1 -1\n");
		return 0;
	}
	for (int i=1;i<=m;i++) c[i]=b[i];
	c[m+1]=1000;
	for (int i=1;i<=n;i++) c[i+m+1]=a[i];
	//for (int i=1;i<=n+m+1;i++) cout<<c[i]<<' ';cout<<endl;
	int j=1;f[1]=0;
	for (int i=2;i<=n+m+1;i++) {
		if (j+f[j]>=i) f[i]=min(f[i-j+1],j+f[j]-i);
		else f[i]=0;
		f[i]=max(f[i],0);
		for (;(f[i]>=0) && (i+f[i]<=n+m+1) && (c[1+f[i]]==c[i+f[i]]);) f[i]++;
		f[i]--;
		if (i+f[i]>j+f[j]) j=i;
	}
	//for (int i=1;i<=n+m+1;i++) cout<<f[i]<<' '<<endl;
	for (int i=n;i>=1;i--) c[n-i+1]=a[i];
	c[n+1]=1000;
	for (int i=1;i<=m;i++) c[i+n+1]=b[i];
	j=1,g[1]=0;
	for (int i=2;i<=n+m+1;i++) {
		if (j+g[j]>=i) g[i]=min(g[i-j+1],j+g[j]-i);
		else g[i]=0;
		g[i]=max(g[i],0);
		for (;(g[i]>=0) && (i+g[i]<=n+m+1) && (c[1+g[i]]==c[i+g[i]]);) g[i]++;
		g[i]--;
		if (i+g[i]>j+g[j]) j=i; 
	}
	int r=0;
	for (int i=1;(i<n-1) && (a[i]==b[m-i+1]);i++) st[++r]=i;
	if (!r) {
		printf("-1 -1\n");
		return 0;
	}
	sort(st+1,st+r+1,cmp);
	for (m1=1;m1<=n+m+2;m1<<=1) ;
	for (int i=m1+m1;i>=1;i--) s[i]=oo;
	int ansi=-1,ansj=-1;
	if (f[n+1+1]==n-1) ansi=n-1,ansj=n;
	for (int i=1,k=1;i<=r;i++) {
		if (f[st[i]+1+m+1]<0) continue;
		for (;(k+1<=n) && (k<=min(1+f[st[i]+1+m+1],n-st[i]));k++) change(k+1+g[k+1+n+1],k);
		int tmp=ask(n-st[i],n+m);
		//cout<<st[i]<<' '<<k<<endl;
		if (tmp>=oo) continue;
		if (st[i]>ansi) ansi=st[i],ansj=tmp+st[i]+1;
	}
	printf("%d %d\n",ansi-1,ansj-1);
	return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值