codeforces-edu131-E-Text Editor


Text Editor

题目传送门

题目大意

给出两个字符串 $a$ 和 $s$ ,长度分别为 $n$ 和 $m$ ,且 $1≤m≤n≤5000$ ,问经过最少多少次操作能从 $a$ 串变成 $s$ 串,如果不能输出 $-1$ 。初始时光标位于 $a$ 串末尾。有几种操作

  • “left”,光标向前移动一格
  • “right”,光标向后移动一格
  • “home”,光标移动到最前面
  • “end”,光标移动到最后面
  • “backspace”,删除

思路

考虑每一个公共子串,这个公共子串的右边使用 backspace 直到该公共子串的右侧,而左侧使用 home 先移到最左边,再进行 rightbackspace 操作,直到该公共子串的左侧。

以此遍历每一个公共子串,每次更新 $ans$ 的值,取 $min$ ,由此得到的最终结果即为所求,相当于是一个 $n^2$ 的 $dp$ 。当然,$ans$ 的值首先要初始化为一路从右到左 backspace 的操作数。

然而,要进行这样的操作,需要知道这个公共子串的左侧能不能满足题目要求,即左侧剩余的字符串能否互相匹配上,右侧同理,因此,我们还需要维护两个数组 $pre[i]$ 和 $suf[i]$ 分别表示在从左到右和从右到左的顺序下,首先能匹配上的下标。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<set>
#include<map>
#include<cmath>
#include<iostream>
#include<unordered_set>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Inf 0x3f3f3f3f
//#define int long long
using namespace std;
typedef long long LL;
typedef pair<int,int> P;
typedef pair<P,int> PP;
const int MAXX=5005;
const double eps=0.0000001;
const LL mod=1000000007;

int n,m,pre[MAXX],suf[MAXX];
char a[MAXX],s[MAXX];
int len[MAXX][MAXX];

inline void solve_it(){
scanf("%d%d",&n,&m);
getchar();scanf("%s",a+1);
getchar();scanf("%s",s+1);
bool flag=false;
int first=0;
for(int i=1,j=1;i<=n;++i){
if(a[i]==s[j]){
pre[j]=i;
++j;
}
else if(!first)
first=i;
if(j>m)
flag=true;
}
if(!flag){
printf("-1\n");
return;
}
if(n==m){
printf("0\n");
return;
}
for(int i=n,j=m;i>=1;--i){
if(a[i]==s[j]){
suf[j]=i;
--j;
}
if(j==0)
break;
}

for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
len[i][j]=((a[i]==s[j])?(len[i-1][j-1]+1):(0));
int n1=n+1,m1=m+1;
for(int i=1;i<=n1;++i)
len[i][m1]=0;
for(int i=1;i<=m1;++i)
len[n1][i]=0;

int ans=n-first+1;
for(int j=1;j<=m;++j)
for(int i=pre[i];i<=n;++i){
if(len[i+1][j+1]) continue;
int lenn=len[i][j];
if((j==m||suf[j+1]>i)&&(j-lenn==0||pre[j-lenn]<i-lenn)){
int jj=n-i;
if(i>j)
jj+=i-j+i-lenn+1;
ans=min(ans,jj);
}
}

printf("%d\n",ans);
}

signed main(){
LL T;scanf("%lld",&T);
while(T--)
solve_it();

return 0;
}

补充

此题也可以使用扩展KMP,具体为使用扩展KMP代替上面的求公共子串部分,可以达到一样的效果,但是感觉没有上面的简便。


文章作者: Shaun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Shaun !
评论
  目录