[题目][蓝桥杯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  记忆化搜索与动态规划

原文地址:https://www.cnblogs.com/jinkun113/p/13933879.html