1. 程式人生 > 實用技巧 >[題目][藍橋杯ALGO-88] 字元統計

[題目][藍橋杯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.2 字串 Hash

5.3 KMP / 擴充套件 KMP 演算法

5.5 字尾陣列 SA /字尾自動機 SAM /字尾樹

4.1 記憶化搜尋與動態規劃