[題目][藍橋杯ALGO-88] 字元統計
一、題目
1、題目連結
http://lx.lanqiao.cn/problem.page?gpid=T220(需要登入且需要 VIP 賬戶)
1、問題描述
給定一個長度為 n 的字串 S,還有一個數字 L,統計長度大於等於 L 的出現次數最多的子串(不同的出現可以相交),如果有多個,輸出最長的,如果仍然有多個,輸出第一次出現最早的。
2、輸入格式
第一行一個數字L。
第二行是字串S。
3、輸出格式
一行,題目要求的字串。
4、樣例輸入
4
bbaabbaaaaa
5、樣例輸出
bbaa
6、資料範圍
n <= 60,L大於0,且不超過S的長度。
二、分析與思路
本來是一道大水題,但突然想把所有可以的思路都列出來一下,而且水的原因主要是資料範圍很小,調大了自然需要更優的演算法。
1、O(n ^ 4) 純暴力列舉
先列舉子串長度,再列舉子串起始位置,再列舉該位置之後的子串的起始位置,並逐位進行比較,總共需要 4 重 for 迴圈,即 O(n ^ 4) 的時間複雜度,無需過多分析。
2、O(n ^ 3) 暴力列舉 + KMP
在純暴力列舉的基礎上優化,列舉完子串起始位置後,對於每一個子串採用 KMP 演算法判斷相同子串的個數,時間複雜度為 O(n ^ 3)。
3、O(n ^ 3) 動態規劃
設 f[i][j][k] 表示 “長度為 i,起始位置分別為 j 和 k 的兩個子串是否相等”,相等為 1,反之為 0。
4、O(n ^ 2 log n) 字串 Hash
記錄所有子串的 Hash 值,並記錄每個 Hash 值出現的次數。
時間複雜度肯定是很優的,但這道題還需要判斷長度和起始位置以滿足要求,需要開一個類來儲存。
另外程式碼中並沒有記錄每個 Hash 值的出現次數,而是將所有值排序,直接判斷連續相同的數的個數,省去了用 map 對映 Hash 值。
5、O(n ^ 2) 字尾陣列
暫略
三、程式碼
程式碼 1(純暴力列舉)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int MAXN = 60 + 5; 5 6 int l, la, ansl, nob, tot, mx; 7 char a[MAXN], b[MAXN], ans[MAXN];8 9 int main() { 10 cin >> l >> a + 1, la = strlen(a + 1); 11 for (int i = la; i >= l; i--) 12 for (int j = 1; j <= la - i + 1; j++) { 13 tot = 0; 14 memset(b, 0, sizeof(b)); 15 for (int k = j; k <= j + i - 1; k++) 16 b[k - j + 1] = a[k]; 17 for (int k = 1; k <= la - i + 1; k++) { 18 nob = 0; 19 for (int o = k; o <= k + i - 1; o++) 20 if (a[o] != b[o - k + 1]) { 21 nob = 1; 22 break; 23 } 24 tot += (!nob); 25 } 26 if (tot > mx) { 27 mx = tot; 28 for (int k = 1; k <= i; k++) 29 ans[k] = b[k]; 30 ansl = i; 31 } 32 } 33 for (int i = 1; i <= ansl; i++) 34 cout << ans[i]; 35 return 0; 36 }
程式碼 2(暴力列舉 + KMP)
#include <bits/stdc++.h> using namespace std; const int MAXN = 60 + 5; int l, la, ansl, nob, tot, mx, f[MAXN]; char a[MAXN], b[MAXN], ans[MAXN]; int main() { cin >> l >> a + 1, la = strlen(a + 1); for (int i = la; i >= l; i--) for (int j = 1; j <= la - i + 1; j++) { tot = 0; memset(b, 0, sizeof(b)); for (int k = j; k <= j + i - 1; k++) b[k - j + 1] = a[k]; memset(f, 0, sizeof(f)); f[0] = -1; for (int k = 1, x = -1; k <= i; k++) { while (x >= 0 && b[x + 1] != b[k]) x = f[x]; f[k] = ++x; } for (int k = 1, x = 0; k <= la; k++) { while (x >= 0 && b[x + 1] != a[k]) x = f[x]; tot += (++x == i); } if (tot > mx) { mx = tot; for (int k = 1; k <= i; k++) ans[k] = b[k]; ansl = i; } } for (int i = 1; i <= ansl; i++) cout << ans[i]; return 0; }
程式碼 3(動態規劃)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int MAXN = 60 + 5; 5 6 int l, la, ansl, mx, f[MAXN][MAXN][MAXN]; 7 char a[MAXN], ans[MAXN]; 8 9 int main() { 10 cin >> l >> a + 1, la = strlen(a + 1); 11 for (int i = 1; i <= la; i++) 12 for (int j = 1; j <= la; j++) 13 f[0][i][j] = 1; 14 for (int i = 1; i <= la; i++) 15 for (int j = 1; j <= la - i + 1; j++) { 16 int o = 0; 17 for (int k = j + 1; k <= la - i + 1; k++) 18 if (f[i - 1][j][k] && a[j + i - 1] == a[k + i - 1]) 19 f[i][j][k] = 1, o++; 20 if (o > mx && i >= l || o == mx && i > ansl) { 21 mx = o, ansl = i; 22 for (int k = 1; k <= i; k++) 23 ans[k] = a[j + k - 1]; 24 } 25 } 26 for (int i = 1; i <= ansl; i++) 27 cout << ans[i]; 28 return 0; 29 }
程式碼 4(字串 Hash)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 6 const ll MOD = 1e9 + 7; 7 const ll MAXN = 60 + 5; 8 const ll INF = 1 << 30; 9 const ll x = 131; 10 11 ll l, la, ansl, tot, cnt, mx, anss = INF, fir; 12 char a[MAXN], ans[MAXN]; 13 14 class Data { 15 public: 16 ll v, l, s; 17 bool operator < (const Data& o) const { 18 return v < o.v; 19 } 20 } d[MAXN * MAXN]; 21 22 int main() { 23 cin >> l >> a + 1, la = strlen(a + 1); 24 for (int i = 1; i <= la; i++) { 25 ll o = 0; 26 for (int j = i; j <= la; j++) { 27 o = (o * x + a[j]) % MOD; 28 if (j - i + 1 >= l) 29 d[++tot] = (Data) {o, j - i + 1, i}; 30 } 31 } 32 sort(d + 1, d + tot + 1); 33 for (int i = 1; i <= tot; i++) 34 if (d[i].v == d[i - 1].v) fir = min(fir, d[i - 1].s), cnt++; 35 else { 36 fir = min(fir, d[i - 1].s); 37 if (cnt > mx || cnt == mx && d[i - 1].l > ansl || 38 cnt == mx && d[i - 1].l == ansl && fir < anss) 39 mx = cnt, ansl = d[i - 1].l, anss = fir; 40 cnt = 1, fir = INF; 41 } 42 for (int i = 1; i <= ansl; i++) 43 cout << a[anss + i - 1]; 44 return 0; 45 }
四、相關知識點
5.3 KMP / 擴充套件 KMP 演算法
5.5 字尾陣列 SA /字尾自動機 SAM /字尾樹