洛谷P2375 动物园

我要死了。这是我做过的最恶心的题之一。

天下第一的大毒瘤。有gay毒。

我不如熊猫好多年...


题意:给定字符串,求g[i],表示:[0, i]中满足该子串既是前缀又是后缀还不重叠的子串数。

解:题面都写了KMP,想必跟KMP有关。

然后我痛苦的思考了1天无果......

我首先想到求出nex[]和f[],f[]表示可重叠的既是前缀又是后缀的子串数。

然后想找出f[]和g[]的关系,失败。

然后想到再来一个len[]表示f[]最长的那一个的长度,还是不行...

被折磨了一天,撑不住了,在吃完饭的时候开始看题解。

发现:我们要魔改KMP,每次跳完增加之后若大于一半,就再跳...

这时你停住的地方j就是恰好比一半小的最大值。

然后如果i这里的nex为0,j显然为0,g[]显然也为0

否则j不为0,这时你把f[j - 1]加1就是g[i]

你加的1就是[0, j - 1]这个串。

然后j可以直接继承到下一个i

因为你的j和KMP里的j差不多,除了不超过一半之外都一样。

然后你i每次 + 1的时候g[]最多 + 1

这样就搞过了这个大毒瘤题....

(事实上还不是很清楚)

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 typedef long long LL;
 5 
 6 const int N = 1000010, MO = 1e9 + 7;
 7 
 8 int nex[N], f[N], g[N];
 9 char s[N];
10 
11 inline void solve() {
12     scanf("%s", s);
13     int n = strlen(s);
14     nex[0] = 0;
15     f[0] = 0;
16     for(int i = 1, j = 0; i < n; i++) {
17         while(j && s[i] != s[j]) {
18             j = nex[j - 1];
19         }
20         if(s[i] == s[j]) {
21             j++;
22         }
23         nex[i] = j;
24         if(j) {
25             f[i] = f[j - 1] + 1;
26         }
27         else {
28             f[i] = 0;
29         }
30     }
31 
32     g[0] = 0;
33     LL ans = 1;
34     for(int i = 1, j = 0; i < n; i++) {
35         while(j && s[i] != s[j]) {
36             j = nex[j - 1];
37         }
38         if(s[i] == s[j]) {
39             j++;
40         }
41         while(2 * j > (i + 1)) {
42             j = nex[j - 1];
43         }
44         if(!j) {
45             g[i] = 0;
46         }
47         else {
48             g[i] = f[j - 1] + 1;
49         }
50         ans = (ans * (g[i] + 1)) % MO;
51     }
52     printf("%lld\n", ans);
53     return;
54 }
55 
56 int main() {
57     int T;
58     scanf("%d", &T);
59     while(T--) {
60         solve();
61     }
62     return 0;
63 }
AC代码

[update]首先有个30分暴力想必大家都会。

说一下SAM做法。你考虑一个border是什么样的,显然前缀是一条链,而后缀是该点在fail树上到根的链。于是相当于求这两个东西的交,还有个深度限制。

给前缀链标记为1,非前缀标记为0,并统计到根路径的点权和。这样,我们只要找到每个点在fail树上满足限制的最深的点,就能知道答案了。

考虑到每个点在fail树上向下走的时候,满足限制的最深点也是单调向下的。于是拿一个栈维护当前点到根的链,用一个单调指针在栈上找就行了。

  1 #include <bits/stdc++.h>
  2 
  3 #define add(x, y) edge[++tp].v = y; edge[tp].nex = e[x]; e[x] = tp
  4 
  5 const int N = 2000010, MO = 1e9 + 7;
  6 
  7 struct Edge {
  8     int nex, v;
  9 }edge[N]; int tp;
 10 
 11 int n, tr[N][26], fail[N], len[N], e[N], vis[N], Time, val[N], stk[N], top, ans, tot = 1, last = 1;
 12 char str[N];
 13 std::queue<int> Q;
 14 /*
 15 2
 16 abcababc
 17 abcababc
 18 */
 19 namespace bf {
 20     typedef unsigned long long uLL;
 21     const uLL B = 131;
 22     uLL pw[300], h[300];
 23     inline void prework() {
 24         pw[0] = 1;
 25         for(int i = 1; i <= n; i++) {
 26             pw[i] = pw[i - 1] * B;
 27             h[i] = h[i - 1] * B + str[i];
 28         }
 29         return;
 30     }
 31     inline uLL Hash(int l, int r) {
 32         return h[r] - h[l - 1] * pw[r - l + 1];
 33     }
 34     inline void solve() {
 35         prework();
 36         int ans = 1;
 37         for(int i = 1; i <= n; i++) {
 38             /// [1, i]
 39             //printf("i = %d \n", i);
 40             int temp = 1;
 41             for(int t = i >> 1; t; t--) {
 42                 //printf("t = %d \n", t);
 43                 if(Hash(1, t) == Hash(i - t + 1, i)) {
 44                     /// num[i] = t;
 45                     ++temp;
 46                 }
 47             }
 48             ans = 1ll * ans * temp % MO;
 49         }
 50         printf("%d\n", ans);
 51         return;
 52     }
 53 }
 54 
 55 inline void insert(int f) {
 56     int p = last, np = ++tot;
 57     last = np;
 58     len[np] = len[p] + 1;
 59     vis[np] = Time;
 60     while(p && !tr[p][f]) {
 61         tr[p][f] = np;
 62         p = fail[p];
 63     }
 64     if(!p) {
 65         fail[np] = 1;
 66     }
 67     else {
 68         int Q = tr[p][f];
 69         if(len[Q] == len[p] + 1) {
 70             fail[np] = Q;
 71         }
 72         else {
 73             int nQ = ++tot;
 74             len[nQ] = len[p] + 1;
 75             fail[nQ] = fail[Q];
 76             fail[Q] = fail[np] = nQ;
 77             memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
 78             while(tr[p][f] == Q) {
 79                 tr[p][f] = nQ;
 80                 p = fail[p];
 81             }
 82         }
 83     }
 84     return;
 85 }
 86 
 87 inline void clear() {
 88     for(register int i(1); i <= tot; i++) {
 89         memset(tr[i], 0, sizeof(tr[i]));
 90         val[i] = e[i] = fail[i] = len[i] = 0;
 91     }
 92     last = tot = 1;
 93     tp = 0;
 94     return;
 95 }
 96 
 97 void DFS(int x, int p) {
 98     stk[++top] = x;
 99     /// calc ans x
100     //printf("x = %d \n", x);
101     while(p < top && len[stk[p + 1]] * 2 <= len[x]) {
102         ++p;
103     }
104     if(vis[x] == Time) {
105         ans = 1ll * ans * (val[stk[p]] + 1) % MO;
106     }
107     for(int i = e[x]; i; i = edge[i].nex) {
108         int y = edge[i].v;
109         val[y] = val[x] + (vis[y] == Time);
110         DFS(y, p);
111     }
112     --top;
113     return;
114 }
115 
116 inline void solve() {
117     scanf("%s", str + 1);
118     n = strlen(str + 1);
119     /*if(n <= 200) {
120         bf::solve();
121         return;
122     }*/
123     ++Time;
124     ans = 1;
125     for(register int i(1); i <= n; i++) {
126         insert(str[i] - 'a');
127     }
128     for(register int i(2); i <= tot; i++) {
129         add(fail[i], i);
130     }
131 
132     DFS(1, 1); /// get val
133     printf("%d\n", ans);
134     clear();
135     return;
136 }
137 
138 int main() {
139     int T;
140     scanf("%d", &T);
141     while(T--) {
142         solve();
143         if(T) memset(str + 1, 0, n * sizeof(char));
144     }
145     return 0;
146 }
AC代码

这题非常良心的没卡空间。

posted @ 2018-09-22 20:24  huyufeifei  阅读(194)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

ReadEra 阅读书籍

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜