字符串

懶得認真寫博客了...算法

圈圈數組

求每次把字符串所有 + 1 以後的最小表示法。ide

就是那樣,先Hash。ui

+ 1的時候考慮有哪些地方變成0了,在這些位置中O(logn)比較。spa

沒有變成0的話就是上一次的答案。3d

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <vector>
  4 #include <algorithm>
  5 
  6 typedef unsigned long long uLL;
  7 
  8 const int N = 50010, B = 1e9 + 7;
  9 
 10 int s[N << 1], n, m, k;
 11 uLL H[N << 1], po[N << 1];
 12 std::vector<int> pos[N];
 13 
 14 inline void gethash(int n) {
 15     H[0] = s[0];
 16     po[0] = 1;
 17     for(int i = 1; i < n * 2; i++) {
 18         po[i] = po[i - 1] * B;
 19         H[i] = H[i - 1] * B + s[i];
 20     }
 21     return;
 22 }
 23 inline uLL Hash(int l, int r) {
 24     if(l == 0) {
 25         return H[r]; /// error : space
 26     }
 27     return H[r] - H[l - 1] * po[r - l + 1];
 28 }
 29 
 30 inline int getmin() {
 31     int i = 0, j = 1;
 32     while(i < n && j < n) {
 33         int k = 0;
 34         while(s[i + k] == s[j + k] && k < n) {
 35             k++;
 36         }
 37         if(k == n) {
 38             return 0;
 39         }
 40         if(s[i + k] > s[j + k]) {
 41             i += k + 1;
 42             if(i == j) {
 43                 i++;
 44             }
 45         }
 46         else {
 47             j += k + 1;
 48             if(i == j) {
 49                 j++;
 50             }
 51         }
 52     }
 53     return std::min(i, j);
 54 }
 55 
 56 inline bool great(int i, int j, int t) {
 57     int l = 0, r = n, mid;
 58     while(l < r) {
 59         mid = (l + r) >> 1;
 60         if(Hash(i, i + mid) == Hash(j, j + mid)) {
 61             l = mid + 1;
 62         }
 63         else {
 64             r = mid;
 65         }
 66     }
 67     return (s[i + r] + t) % m > (s[j + r] + t) % m;
 68 }
 69 
 70 int main() {
 71     freopen("in.in", "r", stdin);
 72     freopen("my.out", "w", stdout);
 73     scanf("%d%d%d", &n, &m, &k);
 74     k--;
 75     for(int i = 0; i < n; i++) {
 76         scanf("%d", &s[i]);
 77         pos[s[i]].push_back(i);
 78     }
 79 
 80     memcpy(s + n, s, n * sizeof(int));
 81     gethash(n);
 82 
 83     int t = getmin();
 84     printf("%d\n", s[t + k]);
 85     //printf("%d\n", t);
 86     for(int i = 1; i < m; i++) {
 87         if(pos[m - i].size()) {
 88             t = pos[m - i][0];
 89             for(int j = 1; j < pos[m - i].size(); j++) {
 90                 if(great(t, pos[m - i][j], i)) {
 91                     t = pos[m - i][j];
 92                 }
 93             }
 94         }
 95         printf("%d\n", (s[t + k] + i) % m);
 96         //printf("%d\n", t);
 97     }
 98 
 99     return 0;
100 }
AC代碼

manachercode

做用:求出字符串中,以每一個位置爲中心,最多能向兩邊擴展多少迴文串。blog

例:  #a#b#a#排序

f[]:  0 1 0 3 0 1 0ci

兩邊中間都要補上#之類的東西,max(f)直接輸出就是ans

模板:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 const int N = 11000010;
 6 
 7 char s[N << 1];
 8 int f[N << 1];
 9 
10 int main() {
11     scanf("%s", s);
12     int n = strlen(s);
13     n = (n << 1) + 1;
14     for(int i = n - 1; i >= 0; i--) {
15         if(i & 1) {
16             s[i] = s[i >> 1];
17         }
18         else {
19             s[i] = '#';
20         }
21     }
22     //printf("%s\n", s);
23     f[0] = 1;
24     int p = 0, ans = 1;
25     for(int i = 1; i < n; i++) {
26         int t = 2 * p - i;
27         if(t < 0 || i + f[t] >= p + f[p]) {
28             int j = p + f[p] - i + 1;
29             while(i + j < n && i - j >= 0 && s[i + j] == s[i - j]) {
30                 j++;
31             }
32             f[i] = j - 1;
33         }
34         else {
35             f[i] = f[t];
36         }
37         //printf("%d %d\n", i, f[i]);
38         if(i + f[i] > p + f[p]) {
39             p = i;
40         }
41         ans = std::max(ans, f[i]);
42     }
43     printf("%d", ans);
44     return 0;
45 }
mancher模板

 這個是hash替代manacher,能夠求出以每一個位置爲結尾的最長迴文串。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 typedef unsigned long long uLL;
 6 
 7 const int N = 11000010, B = 13331;
 8 
 9 int f1[N], f2[N], n;
10 char s[N];
11 uLL H1[N], H2[N], po[N];
12 
13 inline void gethash() {
14     H1[0] = s[0];
15     po[0] = 1;
16     for(int i = 1; i < n; i++) {
17         H1[i] = H1[i - 1] * B + s[i];
18         po[i] = po[i - 1] * B;
19     }
20     H2[n - 1] = s[n - 1];
21     for(int i = n - 2; i >= 0; i--) {
22         H2[i] = H2[i + 1] * B + s[i];
23     }
24     return;
25 }
26 inline uLL Hash1(int l, int r) {
27     if(!l) {
28         return H1[r];
29     }
30     return H1[r] - H1[l - 1] * po[r - l + 1];
31 }
32 inline uLL Hash2(int l, int r) {
33     if(r == n - 1) {
34         return H2[l];
35     }
36     return H2[l] - H2[r + 1] * po[r - l + 1];
37 }
38 
39 int main() {
40     scanf("%s", s);
41     n = strlen(s);
42     gethash();
43     f1[0] = 1;                   //  01234567
44     int ans = 1;                 //  babcbabc
45     for(int i = 1; i < n; i++) { //  11313575
46         if(f1[i - 1] < i && s[i] == s[i - f1[i - 1] - 1]) {
47             f1[i] = f1[i - 1] + 2;
48         }
49         else {
50             int k = i - f1[i - 1];
51             while(Hash1(k, i) != Hash2(k, i)) {
52                 k++;
53             }
54             f1[i] = i - k + 1;
55         }
56         ans = std::max(ans, f1[i]);
57         //printf("%d %d \n", i, f1[i]);
58     }
59     printf("%d", ans);
60     return 0;
61 }
AC代碼

最長雙迴文串。

這個很奇怪...O(n)的算法,luogu上面跑的飛起,bzoj就T。

先放洛谷AC代碼吧。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 typedef unsigned long long uLL;
 6 
 7 const int N = 11000010, B = 13331;
 8 
 9 int f1[N], f2[N], n;
10 char s[N];
11 uLL H1[N], H2[N], po[N];
12 
13 inline void gethash() {
14     H1[0] = s[0];
15     po[0] = 1;
16     for(int i = 1; i < n; i++) {
17         H1[i] = H1[i - 1] * B + s[i];
18         po[i] = po[i - 1] * B;
19     }
20     H2[n - 1] = s[n - 1];
21     for(int i = n - 2; i >= 0; i--) {
22         H2[i] = H2[i + 1] * B + s[i];
23     }
24     return;
25 }
26 inline uLL Hash1(int l, int r) {
27     if(!l) {
28         return H1[r];
29     }
30     return H1[r] - H1[l - 1] * po[r - l + 1];
31 }
32 inline uLL Hash2(int l, int r) {
33     if(r == n - 1) {
34         return H2[l];
35     }
36     return H2[l] - H2[r + 1] * po[r - l + 1];
37 }
38 
39 int main() {
40     scanf("%s", s);
41     n = strlen(s);
42     gethash();                   //  01234567
43     f1[0] = f2[n - 1] = 1;       //  babcbabc
44                                  //  75353111
45     for(int i = 1; i < n; i++) { //  11313575
46         if(f1[i - 1] < i && s[i] == s[i - f1[i - 1] - 1]) {
47             f1[i] = f1[i - 1] + 2;
48         }
49         else {
50             int k = i - f1[i - 1];
51             while(Hash1(k, i) != Hash2(k, i)) {
52                 k++;
53             }
54             f1[i] = i - k + 1;
55         }
56     }
57     for(int i = n - 2; i >= 0; i--) {
58         if(i + f2[i + 1] + 1 < n && s[i] == s[i + f2[i + 1] + 1]) {
59             f2[i] = f2[i + 1] + 2;
60         }
61         else {
62             int k = i + f2[i + 1];
63             while(Hash1(i, k) != Hash2(i, k)) {
64                 k--;
65             }
66             f2[i] = k - i + 1;
67         }
68     }
69 
70     int ans = 2;
71     for(int i = 1; i < n; i++) {
72         ans = std::max(ans, f1[i - 1] + f2[i]);
73     }
74     printf("%d", ans);
75     return 0;
76 }
洛谷P4555 AC代碼

 咳咳,問題查出來了,N忘了改,開了1100 0000的uLL數組。。。BZ MLE顯示TLE


POJ1509

poj的毒瘤也不是第一次見了,莫名其妙的WA,而後就A...

最小表示法模板題。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 const int N = 10010;
 6 
 7 char s[N << 1];
 8 
 9 int main() {
10     int T;
11     scanf("%d", &T);
12     while(T--) {
13         scanf("%s", s);
14         int n = strlen(s);
15         memcpy(s + n, s, n * sizeof(char));
16         int i = 1, j = 0;
17         while(i < n && j < n) {
18             int k = 0;
19             while(k < n && s[i + k] == s[j + k]) {
20                 k++;
21             }
22             if(k == n) {
23                 break;
24             }
25             if(s[i + k] > s[j + k]) {
26                 i += k + 1;
27                 if(i == j) {
28                     i++;
29                 }
30             }
31             else {
32                 j += k + 1;
33                 if(j == i) {
34                     j++;
35                 }
36             }
37         }
38         printf("%d\n", std::min(i, j) + 1);
39     }
40     return 0;
41 }
AC代碼

 APIO2014 迴文串

PAM業界毒瘤!!!

這句話不得不說,畢竟理解它花了幾分鐘,可是兩天都打不出來...

最後直接摘抄模板,無論了。注意!insert開新點那裏不能先給tr[p][f]賦值。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 typedef long long LL;
 6 
 7 const int N = 300010;
 8 
 9 char s[N];
10 int n;
11 
12 struct PAM {
13     int tr[N][26], cnt[N], fail[N], len[N], num[N];
14     int top, last;
15     inline void init() { // !!!
16         len[0] = 0;
17         len[1] = -1;
18         fail[0] = 1;
19         fail[1] = 1;
20         last = 0;
21         top = 1;
22         return;
23     }
24     inline int getfail(int d, int x) {
25         while(s[d - len[x] - 1] != s[d]) {
26             x = fail[x];
27         }
28         return x;
29     }
30     inline void insert(int d) {
31         int f = s[d] - 'a';
32         int p = getfail(d, last);
33         if(!tr[p][f]) {
34             ++top;
35             len[top] = len[p] + 2; // 長度
36             fail[top] = tr[getfail(d, fail[p])][f]; // 最長 靠右 子迴文串
37             num[top] = num[fail[top]] + 1; // 這個串內靠右 迴文串數
38             tr[p][f] = top; // 兩端 + f 轉移
39         }
40         last = tr[p][f]; // 末尾最長迴文串
41         cnt[last]++; // 該回文串出現次數
42     }
43     inline void count() { // 統計 cnt
44         for(int i = top; i >= 0; i--) {
45             cnt[fail[i]] += cnt[i];
46         }
47     }
48 }pam;
49 
50 int main() {
51     scanf("%s", s);
52     n = strlen(s);
53     pam.init();
54     for(int i = 0; i < n; i++) {
55         pam.insert(i);
56     }
57     pam.count();
58 
59     LL ans = 0;
60     for(int i = 2; i <= pam.top; i++) {
61         ans = std::max(ans, 1ll * pam.len[i] * pam.cnt[i]);
62     }
63     printf("%lld", ans);
64     
65     return 0;
66 }
AC代碼

有個小問題就是char數組的-1位是空...不知道爲何。


SHOI2011 雙倍迴文

這題用迴文自動機秒...

先打了個裸露在外的暴力,T了一個點,而後剪個枝就A了,跑的還賊快...

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 const int N = 500010;
 6 
 7 char s[N];
 8 int n;
 9 
10 struct PAM {
11     int tr[N][26], cnt[N], len[N], num[N], fail[N];
12     int top, last;
13     inline void init() {
14         len[0] = 0;
15         len[1] = -1;
16         fail[0] = 1;
17         fail[1] = 1;
18         top = 1;
19         last = 0;
20         return;
21     }
22     inline int getfail(int d, int x) {
23         while(s[d - len[x] - 1] != s[d]) {
24             x = fail[x];
25         }
26         return x;
27     }
28     inline void insert(int d) {
29         int f = s[d] - 'a';
30         int p = getfail(d, last);
31         if(!tr[p][f]) {
32             ++top;
33             fail[top] = tr[getfail(d, fail[p])][f];
34             len[top] = len[p] + 2;
35             num[top] = num[fail[top]] + 1;
36             tr[p][f] = top;
37         }
38         last = tr[p][f];
39         cnt[last]++;
40         return;
41     }
42     inline void count() {
43         for(int i = top; i >= 0; i--) {
44             cnt[fail[i]] += cnt[i];
45         }
46         return;
47     }
48 }pam;
49 
50 int main() {
51     pam.init();
52     scanf("%d", &n);
53     scanf("%s", s);
54     for(int i = 0; i < n; i++) {
55         pam.insert(i);
56     }
57     int ans = 0;
58     for(int i = pam.top; i > 1; i--) {
59         if(pam.len[i] % 4 || pam.len[i] <= ans) { 
60             continue;
61         }
62         int j = pam.len[i], p = pam.fail[i];
63         while(pam.len[p] << 1 > j) {
64             p = pam.fail[p];
65         }
66         if(pam.len[p] << 1 == j) {
67             ans = std::max(ans, j);
68         }
69     }
70 
71     printf("%d", ans);
72     return 0;
73 }
AC代碼

洛谷P1872 迴文串計數

仔細分析以後也能夠用PAM秒。

只要記錄每一個位置爲結尾的迴文串數量,翻轉以後再統計每一個位置以前的全部迴文串數量,乘起來便可。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 typedef long long LL;
 6 
 7 const int N = 2010;
 8 
 9 char s[N];
10 int n, ans[N], tot;
11 
12 struct PAM {
13     int tr[N][26], len[N], cnt[N], num[N], fail[N];
14     int last, top;
15     inline void init() {
16         len[0] = 0;
17         len[1] = -1;
18         fail[0] = fail[1] = 1;
19         last = 0;
20         top = 1;
21         return;
22     }
23     PAM() {
24         init();
25     }
26     inline int getfail(int d, int x) {
27         while(s[d - len[x] - 1] != s[d]) {
28             x = fail[x];
29         }
30         return x;
31     }
32     inline void insert(int d) {
33         int f = s[d] - 'a';
34         int p = getfail(d, last);
35         if(!tr[p][f]) {
36             ++top;
37             fail[top] = tr[getfail(d, fail[p])][f];
38             len[top] = len[p] + 2;
39             num[top] = num[fail[top]] + 1;
40             tr[p][f] = top;
41         }
42         last = tr[p][f];
43         cnt[last]++;
44         ans[d] = num[last];
45         tot += num[last];
46     }
47     inline void count() {
48         for(int i = top; i >= 0; i--) {
49             cnt[fail[i]] += cnt[i];
50         }
51         return;
52     }
53     inline void clear() {
54         for(int i = 0; i <= top; i++) {
55             for(int j = 0; j < 26; j++) {
56                 tr[i][j] = 0;
57             }
58             len[i] = fail[i] = num[i] = cnt[i] = 0;
59         }
60         init();
61         return;
62     }
63 }pam;
64 
65 int main() {
66     scanf("%s", s);
67     n = strlen(s);
68     for(int i = 0; i < n; i++) {
69         pam.insert(i);
70     }
71     pam.clear();
72     std::reverse(s, s + n);
73     std::reverse(ans, ans + n);
74     tot = 0;
75     LL t = 0;
76     for(int i = 0; i < n - 1; i++) {
77         pam.insert(i);
78         t += 1ll * tot * ans[i + 1];
79         //printf("%d %d\n", tot, ans[i + 1]);
80     }
81     printf("%lld", t);
82     return 0;
83 }
AC代碼

國家集訓隊 拉拉隊排練

嗯,依舊是PAM水題。 

個人想法是把每一個奇迴文串搞一個結構體,排序以後快速冪。

T了最後一個點...因而跑去看題解,發現能夠用桶裝,繼續T最後一個點...

最後發現是輸入的 k 爆int了,因此不知怎地超時的……

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 typedef long long LL;
  6 
  7 const int N = 1000010;
  8 const LL MO = 19930726;
  9 
 10 char s[N];
 11 int n;
 12 LL bin[N];
 13 LL qpow(int aa, int b);
 14 
 15 struct PAM {
 16     int tr[N][26], fail[N], len[N], cnt[N], num[N];
 17     int last, top;
 18     inline void init() {
 19         len[0] = 0;
 20         len[1] = -1;
 21         fail[0] = fail[1] = 1;
 22         last = 0;
 23         top = 1;
 24         return;
 25     }
 26     PAM() {
 27         init();
 28     }
 29 
 30     inline int getfail(int d, int x) {
 31         while(s[d - len[x] - 1] != s[d]) {
 32             x = fail[x];
 33         }
 34         return x;
 35     }
 36     inline void insert(int d) {
 37         int f = s[d] - 'a';
 38         int p = getfail(d, last);
 39         if(!tr[p][f]) {
 40             ++top;
 41             fail[top] = tr[getfail(d, fail[p])][f];
 42             len[top] = len[p] + 2;
 43             num[top] = num[fail[top]] + 1;
 44             tr[p][f] = top;
 45         }
 46         last = tr[p][f];
 47         cnt[last]++;
 48         return;
 49     }
 50     inline void count() {
 51         for(int i = top; i >= 0; i--) {
 52             cnt[fail[i]] += cnt[i];
 53             if(len[i] & 1 && i > 1) {
 54                 bin[len[i]] += cnt[i];
 55             }
 56         }
 57         return;
 58     }
 59 }pam;
 60 
 61 inline LL qpow(int aa, int b) {
 62     LL a = (LL)(aa) % MO;
 63     LL ans = 1;
 64     while(b) {
 65         if(b & 1) {
 66             ans = (ans * a) % MO;
 67         }
 68         a = (a * a) % MO;
 69         b = b >> 1;
 70     }
 71     return ans;
 72 }
 73 
 74 int main() {
 75     //printf("%d", (int)(MO * MO));
 76     LL k;
 77     scanf("%d%lld", &n, &k);
 78     scanf("%s", s);
 79     for(int i = 0; i < n; i++) {
 80         pam.insert(i);
 81     }
 82     pam.count();
 83 /*
 84     LL tp = 0;
 85     for(int i = 2; i <= pam.top; i++) {
 86         tp += pam.cnt[i];
 87     }
 88     if(k > tp) {
 89         printf("-1");
 90         return 0;
 91     }
 92 */
 93     int i = n;
 94     LL ans = 1;
 95     for(int i = n; i && k; i--) {
 96         if(!bin[i]) {
 97             continue;
 98         }
 99         if(bin[i] <= k) {
100             ans = (ans * qpow(i, bin[i])) % MO;
101             k -= bin[i];
102         }
103         else {
104             ans = (ans * qpow(i, k)) % MO;
105             k = 0;
106             break;
107         }
108     }
109 
110     if(k) {
111         ans = -1;
112     }
113     printf("%lld", ans);
114     return 0;
115 }
AC代碼(桶)

 PAM總結:

迴文自動機,一個點表明一種迴文串,支持如下功能:

1,統計全部迴文串數。cnt的2 ~ top求和/途中累加num

2,統計全部本質不一樣迴文串數。top - 1

3,統計全部本質不一樣迴文串出現次數。count()以後的cnt

4,統計以第i位結尾的迴文串個數。途中num

等等...


SAM

這個東西TM比PAM還毒瘤...我真是要被氣死了。

跟PAM同樣調了兩天,一行一行的照抄別人的模板,就是T...

而後把數組開到2n就A了啊啊啊啊啊啊啊毒瘤!!!

模板:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 typedef long long LL;
 5 const int N = 2000010;
 6 char s[N];
 7 int n;
 8 LL ans;
 9 struct SAM {
10     int tr[N][26], fail[N], len[N], cnt[N], bin[N], topo[N];
11     int root, top, last;
12 
13     inline void init() {
14         top = 1;
15         last = 1;
16         root = 1;
17         return;
18     }
19     SAM() {
20         init();
21     }
22 
23     inline void insert(char c) {
24         int f = c - 'a';
25         int p = last, np = ++top;
26         last = np;
27         cnt[np] = 1;
28         len[np] = len[p] + 1;
29         while(p && !tr[p][f]) {
30             tr[p][f] = np;
31             p = fail[p];
32         }
33         if(!p) {
34             fail[np] = root;
35         }
36         else {
37             int Q = tr[p][f];
38             if(len[Q] == len[p] + 1) {
39                 fail[np] = Q;
40             }
41             else {
42                 int nQ = ++top;
43                 len[nQ] = len[p] + 1;
44                 fail[nQ] = fail[Q];
45                 fail[Q] = fail[np] = nQ;
46                 memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
47                 while(tr[p][f] == Q) {
48                     tr[p][f] = nQ;
49                     p = fail[p];
50                 }
51             }
52         }
53         return;
54     }
55     inline void sort() {
56         for(int i = 1; i <= top; i++) {
57             bin[len[i]]++;
58         }
59         for(int i = 1; i <= top; i++) {
60             bin[i] += bin[i - 1];
61         }
62         for(int i = 1; i <= top; i++) {
63             topo[bin[len[i]]--] = i;
64         }
65         return;
66     }
67     inline void cal() {
68         for(int i = top; i; i--) {
69             int x = topo[i];
70             cnt[fail[x]] += cnt[x];
71             if(cnt[x] > 1) {
72                 ans = std::max(ans, 1ll * len[x] * cnt[x]);
73             }
74         }
75         return;
76     }
77 }sam;
78 
79 int main() {
80     scanf("%s", s);
81     n = strlen(s);
82     for(int i = 0; i < n; i++) {
83         sam.insert(s[i]);
84     }
85     sam.sort();
86     sam.cal();
87     printf("%lld", ans);
88 
89     return 0;
90 }
洛谷P3804

hihocoder 1441 

毒瘤...

如何求一個點的right集合?

他的子樹中全部在主鏈上的len( - 1)

這個卡了我很久。。。不能只取葉子,由於iiaaaii,也不能全取,由於ciiaaaii

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 const int N = 100010;
  5 char s[N], p[N];
  6 int n, A[N], num;
  7 
  8 struct SAM {
  9     int tr[N][26], fail[N], len[N], cnt[N], topo[N], bin[N];
 10     int root, top, last;
 11     bool is_new[N];
 12     ///---------------------
 13     struct Edge {
 14         int v, nex;
 15     }edge[N]; int t;
 16     int e[N];
 17     inline void add(int x, int y) {
 18         t++;
 19         edge[t].v = y;
 20         edge[t].nex = e[x];
 21         e[x] = t;
 22         return;
 23     }
 24     ///---------------------
 25     inline void init() {
 26         root = 1;
 27         top = 1;
 28         last = 1;
 29         t = 0;
 30         return;
 31     }
 32     SAM() {
 33         init();
 34     }
 35     inline void insert(char c) {
 36         int f = c - 'a';
 37         int p = last, np = ++top;
 38         last = np;
 39         len[np] = len[p] + 1;
 40         cnt[np] = 1;
 41         is_new[np] = 1;
 42         while(p && !tr[p][f]) {
 43             tr[p][f] = np;
 44             p = fail[p];
 45         }
 46         if(!p) {
 47             fail[np] = root;
 48         }
 49         else {
 50             int Q = tr[p][f];
 51             if(len[Q] == len[p] + 1) {
 52                 fail[np] = Q;
 53             }
 54             else {
 55                 int nQ = ++top;
 56                 fail[nQ] = fail[Q];
 57                 fail[Q] = fail[np] = nQ;
 58                 len[nQ] = len[p] + 1;
 59                 memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
 60                 while(tr[p][f] == Q) {
 61                     tr[p][f] = nQ;
 62                     p = fail[p];
 63                 }
 64             }
 65         }
 66         return;
 67     }
 68     inline void sort() {
 69         for(int i = 1; i <= top; i++) {
 70             bin[len[i]]++;
 71         }
 72         for(int i = 1; i <= top; i++) {
 73             bin[i] += bin[i - 1];
 74         }
 75         for(int i = 1; i <= top; i++) {
 76             topo[bin[len[i]]--] = i;
 77         }
 78         return;
 79     }
 80     inline void build() {
 81         for(int i = 2; i <= top; i++) {
 82             add(fail[i], i);
 83         }
 84         return;
 85     }
 86     inline void DFS(int x) {
 87         for(int i = e[x]; i; i = edge[i].nex) {
 88             int y = edge[i].v;
 89             DFS(y);
 90         }
 91         if(is_new[x]) {
 92             A[++num] = len[x];
 93         }
 94         return;
 95     }
 96 }sam;
 97 
 98 int main() {
 99     scanf("%s", s);
100     n = strlen(s);
101     for(int i = 0; i < n; i++) {
102         sam.insert(s[i]);
103     }
104     sam.build();
105     int T, m;
106     scanf("%d", &T);
107     while(T--) {
108         scanf("%s", p);
109         m = strlen(p);
110         int k = 1;
111         for(int i = 0; i < m; i++) {
112             int f = p[i] - 'a';
113             k = sam.tr[k][f];
114         }
115         sam.DFS(k);
116         int l = sam.len[sam.fail[k]] + 1;
117         int r = sam.len[k];
118         for(int i = A[1] - l; i < A[1]; i++) {
119             putchar(s[i]);
120         }
121         putchar(' ');
122         for(int i = A[1] - r; i < A[1]; i++) {
123             putchar(s[i]);
124         }
125         putchar(' ');
126         std::sort(A + 1, A + num + 1);
127         for(int i = 1; i <= num; i++) {
128             printf("%d ", A[i]);
129         }
130         puts("");
131         num = 0;
132     }
133 
134     return 0;
135 }
AC代碼

hihocoder1445 

求本質不一樣子串數量。

累加len - len[fail] 便可

 1 #include <cstdio>
 2 #include <cstring>
 3 typedef long long LL;
 4 const int N = 2000010;
 5 char s[N];
 6 int n;
 7 
 8 struct SAM {
 9     int tr[N][26], fail[N], len[N];
10     int root, top, last;
11     inline void init() {
12         root = 1;
13         top = 1;
14         last = 1;
15         return;
16     }
17     SAM() {
18         init();
19     }
20 
21     inline void insert(char c) {
22         int f = c - 'a';
23         int p = last, np = ++top;
24         last = np;
25         len[np] = len[p] + 1;
26         while(p && !tr[p][f]) {
27             tr[p][f] = np;
28             p = fail[p];
29         }
30         if(!p) {
31             fail[np] = root;
32         }
33         else {
34             int Q = tr[p][f];
35             if(len[Q] == len[p] + 1) {
36                 fail[np] = Q;
37             }
38             else {
39                 int nQ = ++top;
40                 fail[nQ] = fail[Q];
41                 fail[Q] = fail[np] = nQ;
42                 len[nQ] = len[p] + 1;
43                 memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
44                 while(tr[p][f] == Q) {
45                     tr[p][f] = nQ;
46                     p = fail[p];
47                 }
48             }
49         }
50         return;
51     }
52 }sam;
53 
54 int main() {
55     scanf("%s", s);
56     n = strlen(s);
57     for(int i = 0; i < n; i++) {
58         sam.insert(s[i]);
59     }
60     LL ans = 0;
61     for(int i = 2; i <= sam.top; i++) {
62         ans += sam.len[i] - sam.len[sam.fail[i]];
63     }
64     printf("%lld", ans);
65     return 0;
66 }
AC代碼

hihocoder1449 

分別求長度爲 1 ~ n 的子串中出現最多的那個子串出現的次數。

考慮到SAM的一個點表明多個子串,咱們要支持區間取max

我以爲線段樹可能有問題,就用了O(n)的算法...

後來問了大佬發現線段樹能夠滋磁這種操做。

我是直接跟更新len,最後從大到小掃一遍。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 const int N = 1000010;
 5 char s[N];
 6 int n, large[N];
 7 
 8 struct SAM {
 9     int tr[N << 1][26], fail[N << 1], len[N << 1], cnt[N << 1];
10     int bin[N << 1], topo[N << 1];
11     int root, top, last;
12     inline void init() {
13         top = 1;
14         root = 1;
15         last = 1;
16         return;
17     }
18     SAM() {
19         init();
20     }
21 
22     inline void insert(char c) {
23         int f = c - 'a';
24         int p = last, np = ++top;
25         last = np;
26         len[np] = len[p] + 1;
27         cnt[np] = 1;
28         while(p && !tr[p][f]) {
29             tr[p][f] = np;
30             p = fail[p];
31         }
32         if(!p) {
33             fail[np] = root;
34         }
35         else {
36             int Q = tr[p][f];
37             if(len[Q] ==  len[p] + 1) {
38                 fail[np] = Q;
39             }
40             else {
41                 int nQ = ++top;
42                 len[nQ]  = len[p] + 1;
43                 fail[nQ] = fail[Q];
44                 fail[Q] = fail[np] = nQ;
45                 memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
46                 while(tr[p][f] == Q) {
47                     tr[p][f] = nQ;
48                     p = fail[p];
49                 }
50             }
51         }
52         return;
53     }
54     inline void sort() {
55         for(int i = 1; i <= top; i++) {
56             bin[len[i]]++;
57         }
58         for(int i = 1; i <= top; i++) {
59             bin[i] += bin[i - 1];
60         }
61         for(int i = 1; i <= top; i++) {
62             topo[bin[len[i]]--] = i;
63         }
64         return;
65     }
66     inline void cal() {
67         for(int i = top; i >= 1; i--) {
68             int x = topo[i];
69             cnt[fail[x]] += cnt[x];
70             large[len[x]] = std::max(large[len[x]], cnt[x]);
71         }
72         return;
73     }
74 }sam;
75 
76 int main() {
77     scanf("%s", s);
78     n = strlen(s);
79     for(int i = 0; i < n; i++) {
80         sam.insert(s[i]);
81     }
82     sam.sort();
83     sam.cal();
84     for(int i = n; i >= 1; i--) {
85         large[i] = std::max(large[i], large[i + 1]);
86     }
87     for(int i = 1; i <= n; i++) {
88         printf("%d\n", large[i]);
89     }
90     return 0;
91 }
AC代碼

HAOI2016 找相同字符

求兩個字符串的相同子串的個數。

容許本質相同。

首先對第一個子串建出SAM。

第二個串匹配到i時,統計以第二個串第i位結尾的答案便可。

那麼第i位,匹配到節點p的答案就是:

(當前匹配長度 - len[fail[p]]) * cnt[p] + p的全部父節點的cnt * △len

後半部分能夠預處理出一個ans數組來,拓撲序遞推便可。

  1 #include <cstdio>
  2 #include <cstring>
  3 typedef long long LL;
  4 const int N = 400010;
  5 char s[N], s2[N];
  6 int n, m, ans[N];
  7 struct SAM {
  8     int tr[N][26], fail[N], len[N], cnt[N], bin[N], topo[N];
  9     int top, root, last;
 10     inline void init() {
 11         top = 1;
 12         root = 1;
 13         last = 1;
 14         return;
 15     }
 16     SAM() {
 17         init();
 18     }
 19 
 20     inline void insert(char c) {
 21         int f = c - 'a';
 22         int p = last, np = ++top;
 23         last = np;
 24         len[np] = len[p] + 1;
 25         cnt[np] = 1;
 26         while(p && !tr[p][f]) {
 27             tr[p][f] = np;
 28             p = fail[p];
 29         }
 30         if(!p) {
 31             fail[np] = root;
 32         }
 33         else {
 34             int Q = tr[p][f];
 35             if(len[Q] == len[p] + 1) {
 36                 fail[np] = Q;
 37             }
 38             else {
 39                 int nQ = ++top;
 40                 len[nQ] = len[p] + 1;
 41                 fail[nQ] = fail[Q];
 42                 fail[Q] = fail[np] = nQ;
 43                 memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
 44                 while(tr[p][f] == Q) {
 45                     tr[p][f] = nQ;
 46                     p = fail[p];
 47                 }
 48             }
 49         }
 50         return;
 51     }
 52     inline void sort() {
 53         for(int i = 1; i <= top; i++) {
 54             bin[len[i]]++;
 55         }
 56         for(int i = 1; i <= top; i++) {
 57             bin[i] += bin[i - 1];
 58         }
 59         for(int i = 1; i <= top; i++) {
 60             topo[bin[len[i]]--] = i;
 61         }
 62         return;
 63     }
 64     inline void cal() {
 65         for(int i = top; i; i--) {
 66             int x = topo[i];
 67             cnt[fail[x]] += cnt[x];
 68         }
 69         for(int i = 1; i <= top; i++) {
 70             ans[i] = cnt[i] * (len[i] - len[fail[i]]);
 71         }
 72         for(int i = 1; i <= top; i++) {
 73             int x = topo[i];
 74             ans[x] += ans[fail[x]];
 75         }
 76         return;
 77     }
 78 }sam;
 79 int main() {
 80     scanf("%s", s);
 81     scanf("%s", s2);
 82     n = strlen(s);
 83     m = strlen(s2);
 84     for(int i = 0; i < n; i++) {
 85         sam.insert(s[i]);
 86     }
 87     sam.sort();
 88     sam.cal();
 89     int p = 1, len = 0;
 90     LL t = 0;
 91     for(int i = 0; i < m; i++) {
 92         int f = s2[i] - 'a';
 93         while(p != 1 && !sam.tr[p][f]) {
 94             p = sam.fail[p];
 95             len = sam.len[p];
 96         }
 97         if(sam.tr[p][f]) {
 98             p = sam.tr[p][f];
 99             len++;
100         }
101         if(p != 1) {
102             t += sam.cnt[p] * (len - sam.len[sam.fail[p]]) + ans[sam.fail[p]];
103         }
104     }
105     printf("%lld", t);
106     return 0;
107 } 
AC代碼
相關文章
相關標籤/搜索