Description
给你 $n$ 个字符串 $S_1$ ~ $S_n$,定义两个字符串 $A$ 和 $B$ 押韵当且仅当:
现在要你从给出的这 $n$ 个字符串中选出一些,使得选出的任意相邻的两个字符串押韵,问你最多能够选出多少个字符串。
空间限制为 256MB。
Solution
考虑把字符串倒过来建 字典树。
这样的话,对于字典树上是单词结尾的一个位置,他的答案就是以它为根的子树中的最长链 + 次长链(要和最长链没有公共边) + 其他儿子个数。
其中最长链和次长链的长度为这条链上是单词结尾的点的个数。
这种摆法也即先摆最长链,再摆根节点和根节点的其他儿子,最后再摆次长链。
最长链和次长链的维护和转移的具体实现见 Code 部分。
这个题的空间给的十分恶心,我迫不得已用了 unordered_map,各种卡常都过不去,最后只能够开各种编译优化来卡过去。
Code
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 
 | #pragma GCC optimize(2)#pragma GCC optimize(3)
 #pragma GCC optimize("Ofast")
 #pragma GCC optimize("inline")
 #pragma GCC optimize("-fgcse")
 #pragma GCC optimize("-fgcse-lm")
 #pragma GCC optimize("-fipa-sra")
 #pragma GCC optimize("-ftree-pre")
 #pragma GCC optimize("-ftree-vrp")
 #pragma GCC optimize("-fpeephole2")
 #pragma GCC optimize("-ffast-math")
 #pragma GCC optimize("-fsched-spec")
 #pragma GCC optimize("unroll-loops")
 #pragma GCC optimize("-falign-jumps")
 #pragma GCC optimize("-falign-loops")
 #pragma GCC optimize("-falign-labels")
 #pragma GCC optimize("-fdevirtualize")
 #pragma GCC optimize("-fcaller-saves")
 #pragma GCC optimize("-fcrossjumping")
 #pragma GCC optimize("-fthread-jumps")
 #pragma GCC optimize("-funroll-loops")
 #pragma GCC optimize("-fwhole-program")
 #pragma GCC optimize("-freorder-blocks")
 #pragma GCC optimize("-fschedule-insns")
 #pragma GCC optimize("inline-functions")
 #pragma GCC optimize("-ftree-tail-merge")
 #pragma GCC optimize("-fschedule-insns2")
 #pragma GCC optimize("-fstrict-aliasing")
 #pragma GCC optimize("-fstrict-overflow")
 #pragma GCC optimize("-falign-functions")
 #pragma GCC optimize("-fcse-skip-blocks")
 #pragma GCC optimize("-fcse-follow-jumps")
 #pragma GCC optimize("-fsched-interblock")
 #pragma GCC optimize("-fpartial-inlining")
 #pragma GCC optimize("no-stack-protector")
 #pragma GCC optimize("-freorder-functions")
 #pragma GCC optimize("-findirect-inlining")
 #pragma GCC optimize("-fhoist-adjacent-loads")
 #pragma GCC optimize("-frerun-cse-after-loop")
 #pragma GCC optimize("inline-small-functions")
 #pragma GCC optimize("-finline-small-functions")
 #pragma GCC optimize("-ftree-switch-conversion")
 #pragma GCC optimize("-foptimize-sibling-calls")
 #pragma GCC optimize("-fexpensive-optimizations")
 #pragma GCC optimize("-funsafe-loop-optimizations")
 #pragma GCC optimize("inline-functions-called-once")
 #pragma GCC optimize("-fdelete-null-pointer-checks")
 #include <unordered_map>
 #include <cstdio>
 #include <cstring>
 #define maxN 3000010
 #define max(x, y) ((x) > (y) ? (x) : (y))
 std::unordered_map<int, int> son[26];
 struct edge{ int x, y, g; } b[maxN];
 int Ans = 0, len = 0, tot = 0;
 char st[maxN];
 bool cu[26][maxN];
 int f[maxN], g[maxN];
 int sum[maxN], h[maxN], val[maxN];
 void ins (int x, int y)
 {
 len++;
 b[len].x = x;
 b[len].y = y;
 b[len].g = h[x];
 h[x] = len;
 }
 void dfs (int x)
 {
 f[x] = g[x] = 1;
 for(int i = h[x];i;i = b[i].g)
 {
 int y = b[i].y;
 dfs(y);
 if(val[y])
 {
 const int X = f[y] + sum[y] + val[y] - 1;
 if(X >= f[x])
 {
 g[x] = f[x];
 f[x] = X;
 }
 else if(X > g[x])
 {
 g[x] = X;
 }
 }
 }
 Ans = max(Ans, f[x] + g[x] + sum[x] + val[x] - 2);
 }
 int main ()
 {
 int n = 0;
 scanf("%d", &n);
 for(int i = 1;i <= n; i++)
 {
 int Len = 0;
 char c = getchar();
 while(c < 'a' || c > 'z')
 {
 c = getchar();
 }
 while(c >= 'a' && c <= 'z')
 {
 st[++Len] = c;
 c = getchar();
 }
 int now = 0;
 int fa = 0;
 for(int j = Len;j >= 1; j--)
 {
 const int X = st[j] - 'a';
 if(!cu[X][now])
 {
 cu[X][now] = true;
 ins(now, son[X][now] = ++tot);
 }
 now = son[X][fa = now];
 }
 sum[fa]++;
 val[now] = 1;
 }
 dfs(0);
 printf("%d", Ans);
 return 0;
 }
 
 |