串
题目背景:
分析:
20% 解法:
暴力枚举前缀,然后两两枚举形成新串,然后有hash判重(用set的宝宝,爆成了5分·····)
50% 解法:
将集合中的所有的字符串构建成一个AC自动机,然后,注意到每个字符串在AC自动机上的匹配路径应该是唯一的,那么我们可以考虑dp,f[i][j][k]表示当前长度为i的字符串,匹配到j状态,第一次的失配的位置在k处,然后直接暴力枚举下一个字符的位置,然后在AC自动机上跑转移就可以了,注意每一次的时的状态的深度一定要大于当前的第一次失配后的匹配长度,否则意味着无法满足题目的信息
100% 解法:
我们可以发现,在上一道题中的在第一次失配前的长度是不需要记录的,那么我们就可以考虑优化状态,f[i][j]表示,当前在第一次失配后的长度,然后当前匹配到的状态,同样注意,每一次失配的状态的深度一定要大于当前的i否则不满足形成前缀。
注意:因为我们一开始就是枚举的在第一次失配后走了多长的长度,但是可能我们可以选择直接在第一次失配后不再往后走,也就是直接将原来的某个字符串的一部分,直接改成一段前缀,想要判断这一个状态是否满足,只需要直接判断当前的fail指针是否为根,如果不是,则就意味着,是可以通过将当前的一段后缀替换为另一段前缀来实现。
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std; const int MAXL = 30 + 5; const int MAXN = 10000 + 10; struct node { node *next[26], *fail; bool exist[26]; int deep; node() { memset(next, 0, sizeof(next)), fail = NULL; memset(exist, 0, sizeof(exist)), deep = 0; } } trie[MAXL * MAXN], *root, *tot = trie; long long dp[MAXL][MAXL * MAXN]; inline void insert(char *s) { int len = strlen(s); node *cur = root; for (int i = 0; i < len; ++i) { if (!cur->next[s[i] - 'a']) cur->next[s[i] - 'a'] = tot++; cur->exist[s[i] - 'a'] = true, cur = cur->next[s[i] - 'a']; } } inline void build_fail() { queue
q; q.push(root), root->deep = 0; while (!q.empty()) { node *cur = q.front(); q.pop(); for (int i = 0; i < 26; ++i) { if (cur->next[i]) { node *v = cur->fail; while (v && !v->next[i]) v = v->fail; cur->next[i]->fail = (v ? v->next[i] : root); q.push(cur->next[i]), cur->next[i]->deep = cur->deep + 1; } else { node *v = cur->fail; while (v && !v->next[i]) v = v->fail; cur->next[i] = (v ? v->next[i] : root); } } } } inline void dp_on_AC_automation() { long long ans = 0; /*判断可以直接通过改变原串的一部分*/ for (node *i = trie; i != tot; ++i) if (i->fail != root) ans++; /*计算需要两串进行拼接的部分的开头位置*/ for (node *i = trie; i != tot; ++i) for (int j = 0; j < 26; ++j) if (!i->exist[j] && i->next[j] != root) dp[1][i->next[j] - trie]++; /*枚举走到的状态和当前的步数*/ for (int i = 1; i < MAXL; ++i) for (node *j = trie; j != tot; ++j) { if (dp[i][j - trie]) { ans += dp[i][j - trie]; for (int k = 0; k < 26; ++k) if (j->exist[k] || j->next[k]->deep > i) dp[i + 1][j->next[k] - trie] += dp[i][j - trie]; } } cout << ans; } int n; char s[MAXL]; int main() { root = new node(); cin >> n; for (int i = 1; i <= n; ++i) cin >> s, insert(s); build_fail(); dp_on_AC_automation(); return 0; }