一道很考验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<<"-- "<