http://acm.hdu.edu.cn/showproblem.php?pid=5510
想了很久队友叫我用ufs + kmp暴力过去了。
fa[x] = y表示x是y的子串,所以只有fa[x] == x才需要kmp一次。
那么这样的话,如果全部都不互为子串的话,复杂度还是爆咋的。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <string> #include <stack> #include <map> #include <set> #include <bitset> #include <cstdlib> #include <ctime> #define X first #define Y second #define clr(u,v); memset(u,v,sizeof(u)); #define in() freopen("data.txt","r",stdin); #define out() freopen("ans","w",stdout); #define Clear(Q); while (!Q.empty()) Q.pop(); #define pb push_back using namespace std; typedef long long ll; typedef pair<int, int> pii; const ll INF = 1e17; const int inf = 0x3f3f3f3f; const int maxn = 2000 + 2; int tonext[502][maxn]; void get_next(char sub[], int tonext[], int lensub) { int i = 1, j = 0; tonext[1] = 0; while (i <= lensub) { if (j == 0 || sub[i] == sub[j]) { tonext[++i] = ++j; } else j = tonext[j]; } } int kmp(char str[], char sub[], int lenstr, int lensub, int tonext[]) { int i = 1, j = 1; while (i <= lenstr) { if (j == 0 || str[i] == sub[j]) { ++i, ++j; } else j = tonext[j]; if (j == lensub + 1) return true; } return false; } char str[502][maxn]; int len[502]; int f; int fa[maxn]; int tofind(int u) { if (fa[u] == u) return u; else return fa[u] = tofind(fa[u]); } void tomerge(int x, int y) { x = tofind(x), y = tofind(y); fa[y] = x; } void work() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%s", str[i] + 1); len[i] = strlen(str[i] + 1); get_next(str[i], tonext[i], len[i]); fa[i] = i; } printf("Case #%d: ", ++f); int ans = -1; for (int i = 2; i <= n; ++i) { for (int j = i - 1; j >= 1; --j) { if (tofind(j) != j) continue; if (kmp(str[i], str[j], len[i], len[j], tonext[j])) { tomerge(i, j); //合并的方向,小的合并去大的 } else { ans = i; } // if (kmp(str[j], str[i], len[j], len[i], tonext[i])) { // tomerge(j, i); // } } } printf("%d ", ans); return; } int main() { #ifdef local in(); #else #endif int t; scanf("%d", &t); while (t--) work(); return 0; }
有一个超时的AC自动机算法。复杂度O(T * n * lenstr * lenstr)
首先所有串buildFail
然后对于每一个串,爬Fail树。从后往前枚举
那么每个串跑一次Fail树的时候就能知道有多少个子串。成立的条件是子串个数 < i,则i位置成立。
如果后面的串使得匹配子串个数变大的话,那么是不影响的,说明在后面枚举的时候那个位置就应该是已经成立的了。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <string> #include <stack> #include <map> #include <set> #include <bitset> #include <cstdlib> #include <ctime> #define X first #define Y second #define clr(u,v); memset(u,v,sizeof(u)); #define in() freopen("2.h","r",stdin); #define out() freopen("ans","w",stdout); #define Clear(Q); while (!Q.empty()) Q.pop(); #define pb push_back using namespace std; typedef long long ll; typedef pair<int, int> pii; const ll INF = 1e17; const int inf = 0x3f3f3f3f; char str[52][2000 + 20]; struct Node { int flag, id; struct Node *Fail; struct Node *pNext[26]; } tree[1000000 + 20]; int t; struct Node * create() { struct Node * p = &tree[t++]; p->flag = 0; p->Fail = NULL; p->id = t - 1; for (int i = 0; i < 26; ++i) p->pNext[i] = NULL; return p; } void toinsert(struct Node **T, char str[]) { struct Node *p = *T; if (p == NULL) p = *T = create(); for (int i = 1; str[i]; ++i) { int id = str[i] - 'a'; if (p->pNext[id] == NULL) { p->pNext[id] = create(); } p = p->pNext[id]; } p->flag++; } struct Node * que[1000000 + 20]; void BuildFlag(struct Node **T) { struct Node *p = *T; struct Node *root = *T; if (p == NULL) return ; int head = 0, tail = 0; que[tail++] = root; while (head < tail) { p = que[head]; for (int i = 0; i < 26; ++i) { if (p->pNext[i] != NULL) { if (p == root) { p->pNext[i]->Fail = root; } else { struct Node * FailNode = p->Fail; while (FailNode != NULL) { if (FailNode->pNext[i] != NULL) { p->pNext[i]->Fail = FailNode->pNext[i]; break; } FailNode = FailNode->Fail; } } que[tail++] = p->pNext[i]; } else if (p == root) { p->pNext[i] = root; } else { p->pNext[i] = p->Fail->pNext[i]; } } head++; } } int vis[1000000 + 20], DFN; int searchAC(struct Node *T, char str[], int val) { DFN++; int ans = 0; struct Node * p = T; struct Node * root = T; if (p == NULL) return 0; for (int i = 1; str[i]; ++i) { int id = str[i] - 'a'; p = p->pNext[id]; struct Node * temp = p; while (temp != root && vis[temp->id] != DFN) { vis[temp->id] = DFN; ans += temp->flag; temp = temp->Fail; if (ans >= val) return false; } } return true; } int f; void work() { t = 0; printf("Case #%d: ", ++f); int n; scanf("%d", &n); struct Node * T = NULL; for (int i = 1; i <= n; ++i) { scanf("%s", str[i] + 1); toinsert(&T, str[i]); } BuildFlag(&T); for (int i = n; i ; --i) { if(searchAC(T, str[i], i)) { printf("%d ", i); return; } } printf("-1 "); return; } int main() { #ifdef LOCAL in(); #else #endif int t; scanf("%d", &t); while (t--) work(); return 0; }