考虑动态规划的作法,设 $dp[i]$ 表示原字符串的前 $i$ 位最少需要删除的字符的数量,显然最后的答案是 $dp[L]$。

对于初始值,因为求最小值,所以先把 $dp[i]$ 均设置为 $+\infty$,同时由转移方程的含义可知 $dp[0] = 0$。然后就是处理单词与文章是否能匹配的核心思路了,设 $dx$ 表示当前匹配到的位置 $i$,$dy$ 表示第 $j$ 个单词的长度 $len[j]$。从 $i$ 与单词的末尾开始,若成功匹配,则 $dx,dy$ 均减一,否则 $dx$ 减一。如果 $dy$ 的值先变为 $0$ 或者 $dx,dy$ 同时变为 $0$,这说明该单词能够在原字符串中匹配;但是如果不能匹配,显然原字符串的第 $i$ 个位置需要被删除。

所以有状态转移方程 $dp[i] = \min (dp[i - 1] + 1,dp[dx] + (i - dx - len[j])$,显然此时的 $dx$ 表示在匹配之后 $dx$ 的值,而不是初始的 $dx = 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
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define init(x) memset (x,INF,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 605;
const int MOD = 1e9 + 7;
inline int read ();
int n,m,dlen[MAX],dp[MAX];
char str[MAX >> 1],cor[MAX][30];
int main ()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
n = read ();m = read ();
scanf ("%s",str + 1);
for (int i = 1;i <= n;++i)
{
scanf ("%s",cor[i] + 1);
dlen[i] = strlen (cor[i] + 1);//单词的长度
}
init (dp);//初始化
dp[0] = 0;
for (int i = 1;i <= m;++i)
{
dp[i] = dp[i - 1] + 1;
for (int j = 1;j <= n;++j)//共有 n 个单词,一一匹配
{
int dx = i,dy = dlen[j];
while (dx && dy)//边界条件
{
if (str[dx] == cor[j][dy]) --dx,--dy;
else --dx;
}
if (!dy) dp[i] = min (dp[i],dp[dx] + (i - dx - dlen[j]));//成功匹配才能进行状态转移
}
}
printf ("%d\n",dp[m]);//此处为 m 而不是 n 哦!
return 0;
}
inline int read ()
{
int s = 0;int f = 1;
char ch = getchar ();
while ((ch < '0' || ch > '9') && ch != EOF)
{
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * f;
}