HDU 6774 String Distance (序列自动机+dp)

题意:给出两个字符串a(1e5)和b(20),再给出q个询问区间l,r,求字符串a下标l到r的子串变为b最少要几次操作,每次操作限定为插入或删除一个字符。

题解:序列自动机+dp
从一个字符串 a [ l , r ] a[l, r] a[l,r]变为另一个字符串 b b b,我们可以先求得两串的 l c s lcs lcs,先删去 a a a中不是 l c s lcs lcs的,共 r − l + 1 − l c s r-l+1-lcs rl+1lcs,再补上使得与 b b b相等,共 m − l c s m-lcs mlcs,所以一共是 r − l + 1 + m − 2 ∗ l c s r-l+1+m-2*lcs rl+1+m2lcs

接下来考虑如何以较短时间求 l c s lcs lcs
正常求法肯定不行,我们可以发现 b b b的长度只有20,那么 l c s lcs lcs最多也就20。
于是我们可以设 d p [ i ] [ l e n ] = j dp[i][len]=j dp[i][len]=j,表示 a [ l , j ] a[l,j] a[l,j] b [ 1 , i ] b[1,i] b[1,i] l c s lcs lcs长度为 l e n len len

但是这样做必须要先预处理出 a a a串从位置 i i i开始某个指定字符的最近位置,我们用序列自动机去求就好了, n x t [ i ] [ j ] nxt[i][j] nxt[i][j]表示从 i i i开始字符 j j j的最近位置。

那么我们就可以得到转移方程:
dp[i][j] = min(dp[i][j], nxt[dp[i - 1][j - 1] + 1][b[i] - 97]);
然后只要dp值即右端点在r内就是lcs。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
char a[maxn], b[22];
int t, q, l, r, la, lb, nxt[maxn][26], dp[22][22];
int main() {
	scanf("%d", &t);
	while (t--) {
		scanf("%s%s", a + 1, b + 1);
		la = strlen(a + 1);
		lb = strlen(b + 1);
		for (int i = 0; i < 26; i++) nxt[la + 1][i] = la + 1;
		for (int i = la; i >= 1; i--) {
			for (int j = 0; j < 26; j++) nxt[i][j] = nxt[i + 1][j];
			nxt[i][a[i] - 97] = i;
		}
		scanf("%d", &q);
		while (q--) {
			scanf("%d%d", &l, &r);
			for (int i = 1; i <= lb; i++) dp[0][i] = la + 1; //不存在
			dp[0][0] = l - 1;  //初始位置
			for (int i = 1; i <= lb; i++) {
				dp[i][0] = l - 1; //不存在
				for (int j = 1; j <= lb; j++) {
					dp[i][j] = dp[i - 1][j];
					if (dp[i - 1][j - 1] < r)
						dp[i][j] = min(dp[i][j], nxt[dp[i - 1][j - 1] + 1][b[i] - 97]);
				}
			}
			int lcs = 0;
			for (int i = lb; i >= 1; i--) {
				if (dp[lb][i] <= r) {
					lcs = i;
					break;
				}
			}
			printf("%d\n", r - l + 1 + lb - 2 * lcs);
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值