题目
http://poj.org/problem?id=3267
题意
有长为L的字符串(L<=600),现在问最少去掉多少个字符,使得字符串由字典中的词不重叠地构成。
字典中含有k(k<=300)个词,每个词长度最多为25
思路
最粗暴的想法:假设当前已经匹配到i为第k个单词的结尾,向前找到第k个单词最早可以开始匹配的位置j,令dp为记载从字符串开始到当前位置完成字典匹配后去掉字符数目的最小值,那么明显dp[i] =dp[j] + i - j - words[k].size()
感想
- TLE,原因是复杂度为O(300 * 300 * 600 * 25),所以后来直接用match从后往前匹配,去掉一重循环
- WA: 原因是忽略了字母匹配之后要向下走一个,恰好实例中单词又没有重复的字母。
Code
Mem:712K Time:1610MS
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int w, l;
string words[601];
char msg[301];
int dp[301];
int start_inds[601];
int match(int end_ind, string word) {
int k = end_ind - 1;
for (int i = (int)word.size() - 1; i >= 0; i--, k--) {
while(k >= 0 && msg[k] != word[i]) k--;
if (i == 0)return k;
}
return -1;
}
int main() {
cin >> w >> l;
cin >> msg;
for (int i = 0; i <= l; i++) {
dp[i] = i;
}
for (int i = 0; i < w; i++) {
cin >> words[i];
}
for (int i = 0; i <= l; i++) {
for (int j = 0; j < i; j++) {
dp[i] = min(dp[i], dp[j] + i - j);
}
for (int k = 0; k < w; k++) {
if (words[k].size() > i)continue;
int largest_start_ind = match(i, words[k]);
start_inds[k] = largest_start_ind;
if (largest_start_ind >= 0 && dp[i] > dp[largest_start_ind] + i - largest_start_ind - (int)words[k].size()) {
dp[i] = dp[largest_start_ind] + i - largest_start_ind - (int)words[k].size();
}
}
}
cout << dp[l] << endl;
return 0;
}