题意:
给定一些 小 字符串,求得能由这些 小 字符串 拼接成的 大 字符串的 前缀 的最大值。
思路:
题目很直白显然,就是 trie 树 和 动态规划。
其实不用 trie 树也可以...但是怎么说呢
以前从来没写过 trie 树,想写一个试试看,于是就写了。
觉得这个时间还是挺有说服力的吧。(只是写得比较丑陋,请多多包涵
Executing... Test 1: TEST OK [0.000 secs, 5160 KB] Test 2: TEST OK [0.000 secs, 5160 KB] Test 3: TEST OK [0.000 secs, 5160 KB] Test 4: TEST OK [0.000 secs, 5160 KB] Test 5: TEST OK [0.000 secs, 5160 KB] Test 6: TEST OK [0.032 secs, 5164 KB] All tests OK.
至于 dp 的部分,从前往后和从后往前都可以
从前往后,can[]记录从第0位到第 i 位是否可被构成。之后对于特定的 i, 向前找已经判断为可以构成的 j, 并且判断 j ~ i 的这一段是否也可被构成,一旦找到一个这样的 j, i就是可行的。
从后往前,dp[]要记录从当前第 i 位开始向后连续的长度。之后对于特定的 i, 向后找所有可以构成的段 i ~ j,然后再加上从 j 开始可以向后延伸的长度,取所有的这些 和 里面的最大值为dp[i]。
个人还是比较偏爱从前向后的,因为比较好想也比较直白嘛(个人见解
那么为什么会有从前往后呢???
因为一开始写跪了啊(叹 第 6 组数据总是超时,于是就 参考 了别人的 blog...
百思不得其解为什么那样的 dp 就可行,为什么我的 dp 就不行,明明时间复杂度的话我应该还更胜一筹的吧(大概
最后最后最后发现,dp时 我忘了加一个约束条件,那就是
j ~ i 的这一段长度 <= 小字符串的最大长度
这是十分重要的,因为缺了这个条件的我的程序,会面临要去找一个长度为 20w 的字符串在不在 trie 树 中的窘境,而事实上在那个样例中,最大需要找的字符串的长度也不超过 10,真是辛苦它了。
所以说细节决定成败,一点不假。
/*
PROB: prefix
LANG: C++
ID: fan_0111
*/
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
using namespace std;
struct node {
char val;
node* bro, *son;
bool flag = false;
};
int tot = 0;
node* init(char ch) {
tot++;
node* p = new node;
p->val = ch;
p->bro = p->son = NULL;
return p;
}
void add(node*& head, string s) {
int len = s.length();
node* fa = NULL, * temp = head;
for (int i = 0; i < len; ++i) {
if (head == NULL) {
head = init(s[i]);
if (tot == 1) temp = head;
if (i == len-1) head->flag = true;
if (fa) fa->son = head;
fa = head;
head = head->son;
continue;
}
else {
node* prev;
while (head && head->val != s[i]) {
prev = head;
head = head->bro;
}
if (head) {
fa = head;
if (i == len-1) head->flag = true;
head = head->son;
}
else {
head = init(s[i]);
if (i == len-1) head->flag = true;
prev->bro = head;
fa = head;
head = head->son;
}
}
}
head = temp;
}
bool find(node* head, string s) {
int len = s.length();
for (int i = 0; i < len; ++i) {
while (head && head->val != s[i]) head = head->bro;
if (!head) return false;
if (i == len-1 && head->flag) return true;
head = head->son;
}
return false;
}
bool can[200020];
int dp[200020];
int main() {
freopen("prefix.in", "r", stdin);
freopen("prefix.out", "w", stdout);
string s;
node* head = NULL;
int maxLen = 0;
while (cin >> s && s[0] != '.') {
add(head, s);
maxLen = max(maxLen, (int)s.length());
}
s = "";
string line;
while (cin >> line) s += line;
int n = s.length();
// for (int i = n-1; i >= 0; --i) {
// for (int j = i; j-i+1 <= maxLen && j < n; ++j) {
// if (!find(head, s.substr(i, j-i+1))) continue;
// if (dp[i] < dp[j+1] + j - i + 1) dp[i] = dp[j+1] + j - i + 1;
// }
// }
// cout << dp[0] << endl;
int maxAns = -1;
for (int i = 0; i < n; ++i) {
for (int j = i; j >= 0 && i-j+1 <= maxLen; --j) {
if ((can[j-1] || j == 0) && find(head, s.substr(j, i-j+1))) {
can[i] = true;
maxAns = max(maxAns, i);
break;
}
}
}
cout << maxAns+1 << endl;
fclose(stdin);
fclose(stdout);
return 0;
}

本文介绍了一种结合Trie树和动态规划的方法来解决字符串拼接问题,详细阐述了从前向后及从后向前两种DP策略,并通过实例解释了正确设置约束条件的重要性。

被折叠的 条评论
为什么被折叠?



