不难发现答案即为a[l, r]的长度加上b的长度减去2倍的a[l,r]与b的LCS。
那么难点就在于如何处理出每次询问的lcs了,这里仍然采用dp的思想。对于dp[i][j]代表b串前i个字符串与a[l,r]形成了长度为j的公共子序列时在a串中的最小下标位置。
那么不难得出转移方程1 :dp[i][j] = min(dp[i][j], dp[i+1][j]) 和 转移方程2:dp[i][j] = min(dp[i][j], a串中dp[i-1][j-1]后面第一个出现b[i]的位置),而某个位置后面第一个字符出现的位置显然可以用序列自动机直接得出。那么这样子复杂度就是26*n + q * m * m,你就可以快乐的ac了。具体实现见代码。
#pragma GCC optimize("-Ofast","-funroll-all-loops") //freopen("C://std/a.in","r",stdin); //freopen("C://std/b.txt","w",stdout); #include<bits/stdc++.h> #define ll long long #define PB push_back #define endl ' ' #define INF 0x3f3f3f3f #define LINF 0x3f3f3f3f3f3f3f3f #define ull unsigned long long #define lson rt << 1, l, mid #define rson rt << 1 | 1, mid + 1, r #define lowbit(x) (x & (-x)) #define rep(i, a, b) for(int i = a ; i <= b ; ++ i) #define per(i, a, b) for(int i = b ; i >= a ; -- i) #define clr(a, b) memset(a, b, sizeof(a)) #define in insert #define random(x) (rand()%x) #define PII(x, y) make_pair(x, y) #define fi first #define se second #define pi acos(-1) #define re register //std::ios::sync_with_stdio(false); using namespace std; const int maxn = 1e6 + 50; const int N = 1e5 + 10; const int M = N * 20; const ll mod = 1e9 + 9; int T, n, m; int nxt[maxn][26], dp[30][30]; char a[maxn], b[maxn]; int solve(int l, int r){ clr(dp, INF); dp[0][0] = l - 1; for(int i = 1 ; i <= m ; ++ i){ dp[i][0] = l - 1; for(int j = 1 ; j <= i ; ++ j){ dp[i][j] = min(dp[i-1][j],dp[i][j]); if(dp[i-1][j-1] < r) dp[i][j] = min(dp[i][j], nxt[dp[i-1][j-1]][b[i]-'a']); } } per(i, 0, m){ rep(j, i, m){ if(dp[j][i] <= r) return i; } } return 0; } signed main(){ cin >> T; int q, l, r; while(T --){ scanf("%s", a + 1); n = strlen(a + 1); scanf("%s", b + 1); m = strlen(b + 1); scanf("%d", &q); clr(nxt, 0x3f); per(i, 1, n){ for(int j = 0 ; j < 26 ; ++ j){ nxt[i-1][j] = nxt[i][j]; } nxt[i-1][a[i]-'a'] = i; } while(q --){ scanf("%d %d", &l, &r); cout << r - l + 1 + m - solve(l, r) * 2 << endl; } } return 0; }