2020 Multi-University Training Contest 2 1012 hdu 6774 String Distance 序列自动机+dp

一道很考验dp功底的题目。思维性强。

首先分析题目,

对于A[l,r],与B[1,m]的距离为:r-l+1+m-2*LCS(A(l,r),B(1,m));

因为最后一定是AB相同,如果用插入操作,一定是在LCS的基础上,B有的字符,且A没有(或者反过来),然后在A这个位置插入一个字符串,次数等价于直接删去。(而同时加两个字符串就更没必要了)。所以等价于AB,字符同时删去到A,B的LCS。

到这里还是比较简单的。

下面是这题的难点:怎么在比较短的时间内维护任意A(l,r)与B的LCS?

这个我也是看题解才会的,字符串dp基本只能想到dp[i][j],表示A[1,i],.B[1,j]进行一些处理。

而这一题显然这样做不行,因为LCS不具有加减性。

我们注意到B的长度m很小,所以可以把复杂度尽量往B的长度靠。

于是自然而然想到维护:dp[i][j]:B[1,i],与A[l,x]的LCS为j,的最小x是多少。

显然只要x小于等于r,那就说明长度j的LCS可以构造出来。

而转移可以借用序列自动机来帮助(就是nxt[i][j],A[i,n]中第一个j字符的位置。一直不知道这玩意叫序列自动机。。)

dp[i][j]=min(dp[i-1][j],nxt[ dp[i-1][j-1] + 1 ] );

注意一些初始化细节即可。

#include 
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
char s[M],t[21];
int dp[21][21];//t字符串 1-i,与 s字符串 [l,x],的LCS为j, 的最小x是多少,(x<=r) 
//结果为L_A+L_B-2*LCS(A,B) 
int nxt[M][26];//s字符串中,第[i,n]中第一个 j+'a' 字符 
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int T;
  	cin>>T;
  	while(T--)
  	{
  		cin>>(s+1)>>(t+1);
  		int n=strlen(s+1),m=strlen(t+1);
  		for(int i=0;i<26;i++)nxt[n+1][i]=n+1;
		for(int i=n;i>=1;i--)
		{
			for(int j=0;j<26;j++)
				nxt[i][j]=nxt[i+1][j];
			nxt[i][s[i]-'a']=i;
		}
		int q;
		cin>>q;
		while(q--)
		{
			int l,r;
			cin>>l>>r;
			for(int i=1;i<=m;i++)dp[0][i]=n+1;
			dp[0][0]=l-1;
			for(int i=1;i<=m;i++)
			{
				dp[i][0]=l-1;
				for(int j=1;j<=m;j++)
				{
					dp[i][j]=dp[i-1][j];
					if(dp[i-1][j-1]=0;j--){
				if(dp[m][j]<=r){
					LCS=j;
					break;
				}
			}
		//	cout<<"--   "<

 

你可能感兴趣的:(2020多校hdu)