HDU-6774 String Distance【2020 Multi-University Training Contest 2】【DP】【LCS】

题目

HDU-6774

题意

a,b两个字符串,a长度10^5,b长度20,
两种操作:
1、选择任意字符串,在任意位置增加一个字符;
2、选择任意字符串,在任意位置删除一个字符;
q次询问,每次给出lr,问对于a[l…r]b两个字符串,最少需要多少次操作可以使两个字符串相同

题解

首先确定不需要增加操作,考虑修改后两个字符串,对于每个位上的字符:
1、如果两个字符串都在这个位置上做了增加操作,那么这个增加操作为多余的,直接不操作即可;
2、如果其中一个字符串在该位置上做了增加操作,那么原先可以直接在另一个字符串的同一位置做删除操作即可,操作数一致;

由于增加操作是多余的,就很容易确定把两个字符串变成相同的最小操作数:|a|+|b|-2*LCS(a,b)
LCS为两个字符串的最大公共子序列的长度

先对进行预处理出pre数组,pre[i][j] 表示 a[i…n]中 j 字符最早出现的位置
对于每次询问用DP求解a[l…r]bLCS
dp[i][j] 表示 b[1…i] 的 LCSj的情况下,在a中的最小前缀的位置
dp[i][j]来源有两个:
1、直接通过i的上一个位置,同样LCS长度为ja的最小前缀得到,即dp[i-1][j];
2、先使用i的上一个位置,LCS长度为j-1a的最小前缀,即dp[i-1][j-1],此时长度为j-1,我们从a字符串中dp[i-1][j-1] 位置开始,向后找到字符b[i] 最早出现位置就是dp[i][j],即pre[dp[i-1][j-1]+1][b[i]]

由此我们可以得到动态转移方程:
dp[ i ][ j ] = min(dp[ i-1 ][ j ], pre[dp[ i-1 ][ j-1 ]+1][b[ i ]])

求出LCS后代入公式就可以求出最小操作数

具体细节对照代码

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<iostream>
#include<sstream>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<bitset>
#include<vector>
#include<limits.h>
#include<assert.h>
#define SZ(X) ((int)(X).size())
#define ALL(X) (X).begin(), (X).end()
#define REP(I, N) for (int I = 0; I < (N); ++I)
#define REPP(I, A, B) for (int I = (A); I < (B); ++I)
#define PER(I, A, B) for (int I = B; I >= A; I--)
#define FOR(I, A, B) for (int I = (A); I <= (B); ++I)
#define FORS(I, S) for (int I = 0; S[I]; ++I)
#define RS(X) scanf("%s", (X))
#define SORT_UNIQUE(c) (sort(c.begin(),c.end()), c.resize(distance(c.begin(),unique(c.begin(),c.end()))))
#define GET_POS(c,x) (lower_bound(c.begin(),c.end(),x)-c.begin())
#define CASET int ___T; scanf("%d", &___T); for(int cs=1;cs<=___T;cs++)
#define MP make_pair
#define PB push_back
#define MS0(X) memset((X), 0, sizeof((X)))
#define MS1(X) memset((X), -1, sizeof((X)))
#define MSINF(X) memset((X), 0x3f3f3f3f, sizeof((X)))
#define LEN(X) strlen(X)
#define F first
#define S a
using namespace std;
typedef long long ll,LL;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<LL> VL;
typedef vector<PII> VPII;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
template<class T> void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(LL &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { scanf(" %c", &x); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template<class T> void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const LL &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template<class T,class U> void _W(const pair<T,U> &x) {_W(x.F); putchar(' '); _W(x.S);}
void W() {}
template<class T, class... U> void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
#ifdef HOME
#define DEBUG(...) {printf("# ");printf(__VA_ARGS__);puts("");}
#else
#define DEBUG(...)
#endif
#define in freopen("in.txt","r",stdin);
#define out freopen("out.txt","w",stdout);
#define mod 998244353
#define INF 0x3f3f3f3f
#define eps 1e-6L
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
LL C(LL n,LL m){if(m > n) return 0;LL ans = 1;FOR(i,1,m){LL a = (n + i - m) % mod;LL b = i % mod;ans = ans * (a * powmod(b, mod-2) % mod) % mod;}return ans;}
ll gcd(ll x,ll y) {return y?gcd(y,x%y):x;}
const int maxn = 111111;

int len_a, len_b;

int pre[maxn][30];

int dp[30][30];

char a[maxn], b[30];

int sol(int l, int r){
	for(int i = 0; i <= len_b; i++){
		for(int j = 0; j <= len_b; j++){
			dp[i][j] = len_a+1;
		}
	}
	for(int i = 0; i < len_b; i++){
		dp[i][0] = l - 1;
	}
	for(int i = 1; i <= len_b; i++){
		for(int j = 1; j <= i; j++){
			dp[i][j] = dp[i-1][j];
			if(dp[i-1][j-1] < r)
				dp[i][j] = min(dp[i][j], pre[dp[i-1][j-1]+1][b[i]]);
		}
	}
	for(int j = len_b; j >= 0; j--){
		if(dp[len_b][j] <= r) {
			return j;
		}
	}
	return 0;
}

int main(){
	int cas;
	scanf("%d", &cas);
	while(cas--){
		a[0] = '#';
		b[0] = '#';
		scanf("%s%s", a+1, b+1);
		len_a = strlen(a) - 1;
		len_b = strlen(b) - 1;
		for(int i = 1; i <= len_a; i++){
			a[i]-='a';
		}
		for(int i = 1; i <= len_b; i++){
			b[i]-='a';
		}
		int q;
		for(int i = 0; i < 26; i++){
			pre[len_a+1][i] = len_a+1;
		}
		for(int i = len_a; i >= 1; i--){
			for(int j = 0; j < 26; j++){
				pre[i][j] = pre[i+1][j];
			}
			pre[i][a[i]] = i;
		}
		scanf("%d", &q);
		while(q--){
			int l, r;
			scanf("%d%d", &l, &r);
			printf("%d\n", len_b + r - l + 1 - 2 * sol(l, r));
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值