题目大意
给出一个主串,和一本字典,问最少在主串删除多少字母,可以使其匹配到字典的单词序列。
是匹配单词序列,而不是一个单词
思路分析
主要是知道状态方程的含义
dp[i]
表示从message中第i
个字符开始,到第L
个字符(结尾处)这段区间所删除的字符数,初始化为 dp[L]=0
由于我的程序是从message尾部向头部检索匹配,所以是下面的状态方程:
if(和字典中的某长度为len的字符匹配成功)dp[i]=min(dp[i],dp[pm]+(pm-i)-len);
else dp[i]= dp[i+1]+1
从程序可以看出,第i
个位置到L
所删除的字符数,总是先取最坏情况,只有可以匹配单词时才进入第二条方程进行状态优化更新。
第一条方程不难理解,只要弄懂 dp[i]
的意义就能简单推导
第二条方程难点在 dp[pm]+(pm-i)-len
从程序知道,pm
是message的指针(其中i
表示当前所匹配的单词在message中的起始位置),pd
是字典的指针
匹配的过程是:
当确认message第i
位和某单词的首位吻合时,就开始逐字匹配,字符相同则两个指针同时向后移动一次,否则pd
固定,pm
移动。当因为 pm>L
跳出匹配时,说明匹配失败,dp[i]
状态不变;当 pd==单词长度
时,单词匹配成功,进行 dp[i]
的状态优化。
显然,匹配成功时,pm-i
代表匹配过程中,从位置i
到pm
的区间长度,再减去单词长度len
,则得到从i
到pm
所删除的字符数(pm-i)-len
。又 dp[pm]
表示从pm
到L
所删除的字符数(根据检索方向,dp[pm]
的值在此前已经被作为最坏打算处理,因此并不是空值)
从而 dp[pm]+(pm-i)-len
表示i
到L
删除的字符数,不难证明这个值一定比 dp[i]
相等或更优,因此取min
赋值给 dp[i]
。
这是本题最难的地方。
最后输出 dp[0]
就可以了,dp[0]
的意思相信大家都明白了。
#define inf 0x3f3f3f3f
#define vec vector<int>
#define P pair<int,int>
#define ll long long
#define MAX 605
int dp[305], W, L;
string s[MAX], t;
int main() {
while (scanf("%d %d", &W, &L) != EOF) {
cin >> t;
for (int i = 0; i < W; i++)cin >> s[i];
memset(dp, 0, sizeof(0));
for (int i = L - 1; i >= 0; i--) {//对每一位进行判断
dp[i] = dp[i + 1] + 1;
for (int j = 0; j < W; j++) {//对字典的每一个串进行判断
int len = s[j].size();
if (L - i >= len && s[j][0]==t[i]) {
//这一个串可能可以匹配上
int i1 = i, i2 = 0;
while (i1 < L&&i2 < len) {
if (t[i1] == s[j][i2])i2++;
i1++;
}
if (i2 == len)//匹配上了
dp[i] = min(dp[i], dp[i1] + i1 - i - len);
}
}
}
cout << dp[0] << endl;
}
}