天天看点

BOI Day2 necklace 题解

题意:

求A,B串连续子串首尾相连形成的项链中,最长的相同的项链长度及子串的初始位置。

项链旋转,翻转后相同就被视为相同。

分析:

首先,两条项链相同,那么它们在原串中有这样的形状:

BOI Day2 necklace 题解

只考虑旋转后相同的字符串,我们可以将其分成两段,分别处理。

对于每个位置对 ( i , p ) (i,p) (i,p),我们希望求出从 i i i向左延伸到最远的 q q q,从 p p p向右延伸到最远的 j j j,使得 A [ q , i ] = B [ p , j ] A[q,i]=B[p,j] A[q,i]=B[p,j],将 L L L记为 g [ i ] [ p ] g[i][p] g[i][p]。

同理,我们还要求出从 i + 1 i+1 i+1向右延伸到最远的 x x x,从 p − 1 p-1 p−1向左延伸到最远的 y y y,使得 A [ i + 1 , x ] = B [ y , p − 1 ] A[i+1,x]=B[y,p-1] A[i+1,x]=B[y,p−1],将它的长度记为 f [ i + 1 ] [ p − 1 ] f[i+1][p-1] f[i+1][p−1]。

这样我们就得到了在 ( i , p ) (i,p) (i,p)分成两段的相同项链的最大长度。

现在的问题变成怎么求 g [ i ] [ p ] g[i][p] g[i][p]和 f [ i + 1 ] [ p − 1 ] f[i+1][p-1] f[i+1][p−1]。

定义一个辅助的数组 d p [ i ] [ j ] dp[i][j] dp[i][j]表示从 i i i向左延伸到最远的 q q q,从 j j j向左延伸到最远的 p p p,使得 A [ q , i ] = B [ p , j ] A[q,i]=B[p,j] A[q,i]=B[p,j],将 L L L记为 d p [ i ] [ j ] dp[i][j] dp[i][j]

如果 A [ i ] = = A [ j ] A[i]==A[j] A[i]==A[j],则 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j]=dp[i-1][j-1]+1 dp[i][j]=dp[i−1][j−1]+1;

否则 d p [ i ] [ j ] = 0 dp[i][j]=0 dp[i][j]=0。

求出 d p [ i ] [ j ] dp[i][j] dp[i][j]后,用 d p dp dp数组算出相应的左端点,更新 g g g数组和 f f f数组。

最后:

g [ i ] [ j ] = m a x ( g [ i ] [ j ] , g [ i ] [ j − 1 ] − 1 ) g[i][j]=max(g[i][j],g[i][j-1]-1) g[i][j]=max(g[i][j],g[i][j−1]−1)

f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i ] [ j + 1 ] − 1 ) f[i][j]=max(f[i][j],f[i][j+1]-1) f[i][j]=max(f[i][j],f[i][j+1]−1)

考虑翻转,将B串翻转过来再做一遍相同的操作即可。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 3005
int dp[MAXN][MAXN],g[MAXN][MAXN],f[MAXN][MAXN],ns,nt,ps,pt,ans;
char s[MAXN],t[MAXN];
void Work(bool flag)
{
	for(int i=max(ns,nt);i>=0;i--) dp[0][i]=dp[i][0]=0;
	for(int i=0;i<=ns;i++)
		for(int j=0;j<=nt;j++) g[i][j]=f[i][j]=0;
	for(int i=1;i<=ns;i++)
		for(int j=1;j<=nt;j++)
			if(s[i]==t[j])
			{
				dp[i][j]=dp[i-1][j-1]+1;
				g[i][j-dp[i][j]+1]=max(g[i][j-dp[i][j]+1],dp[i][j]);
				f[i-dp[i][j]+1][j]=max(f[i-dp[i][j]+1][j],dp[i][j]);
			}
			else dp[i][j]=0;
	for(int i=1;i<=ns;i++)
		for(int j=1;j<=nt;j++)
			g[i][j]=max(g[i][j],g[i][j-1]-1);
	for(int i=1;i<=ns;i++)
		for(int j=nt;j>=1;j--)
			f[i][j]=max(f[i][j],f[i][j+1]-1);
	for(int i=0;i<=ns;i++)
		for(int j=0;j<=nt;j++)
			if(g[i][j+1]+f[i+1][j]>ans)
			{
				ans=g[i][j+1]+f[i+1][j];
				ps=i-g[i][j+1];
				pt=j-f[i+1][j];
				if(flag) pt=nt-pt-ans;
			}
}
int main()
{
	freopen("necklace.in","r",stdin);
	freopen("necklace.out","w",stdout);
	scanf("%s%s",s+1,t+1);
	ns=strlen(s+1),nt=strlen(t+1);
	Work(0);
	reverse(t+1,t+nt+1);
	Work(1);
	printf("%d\n%d %d\n",ans,ps,pt);
}