P5341 [TJOI2019]甲苯先生和大中锋的字符串

题目背景

TJOI2019 D2T2

源文件名:substring.*

时间限制: 1s 内存限制: 128M

题目描述

大中锋有一个长度为 nn 的字符串,他只知道其中的一个子串是祖上传下来的宝藏的密码。但是由于字符串很长,大中锋很难将这些子串一一尝试。

这天大中锋找到甲苯先生算命,但是甲苯先生说:“天机不可泄漏”。

在大中锋的苦苦哀求下,甲苯先生告诉大中锋:“密码是在字符串中恰好出现了 kk 次的子串”。

但是大中锋不知道该怎么做,在大中锋再三的恳求下,甲苯先生看其真诚,又告诉他:“在恰好出现了 kk 次的子串中,你去按照字串的长度分类,密码就在数量最多的那一类里”。

大中锋为了尝试这个密码,想让你帮忙找出子串长度出现次数最多的长度数(如果有多个输出最长长度)。

输入格式

第一行一个正整数 TT ,表示有 TT 组测试数据。

接下来 TT 行每行包含一个字符串和一个正整数 kk 。

输出格式

一共输出 TT 行,每行一个整数表示在出现 kk 次的子串中出现次数的最多的长度。如果不存在子串出现 kk 次,则输出 -11 。

输入输出样例

输入 #1
6
aab 1
abc 1
aaaa 2
abab 2
ababacc 2
abab 4
输出 #1
2
1
3
1
2
-1

说明/提示

数据说明

对于第一个数据:其中子串 b, aa, ab, aabb,aa,ab,aab 均只出现一次,其中长度为 11 的子串出现了 11 次,长度为 22 的子串出现了 22 次,长度为 33 的子串出现了 11 次。所以答案为 22 。

对于第二个数据:其中子串 a, b, c, ab, bc, abca,b,c,ab,bc,abc 均只出现一次,其中长度为 11 的子串出现了 33 次,长度为 22 的子串出现了 22 次,长度为 33 的子串出现了 11 次。所以答案为 11 。

对于第三个数据:其中子串 aaaaaa 出现二次,长度为 33 的子串出现了 11 次,其他长度均没有。所以答案为 33 。

对于第四个数据:其中子串 a, b, aba,b,ab 出现二次,其中长度为 11 的子串出现了 22 次,长度为 22 的子串出现了 11 次。所以答案为 11 。

对于第五个数据:其中子串 b, c, ab, bab,c,ab,ba 出现二次,其中长度为 11 的子串出现了 22 次,长度为 22 的子串出现了 22 次。所以答案为 22 。

对于第六个数据:其中子串没有出现四次。所以本题的本题的答案为 -11 。

数据范围

对于 20\%20% 的数据, 1\leq k\leq n\leq 101kn10

对于 100\%100% 的数据, 1\leq n\leq 10^5,1 \leq T \leq 100,\sum n \leq 3 * 10^61n105,1T100,n3106 ,输入的字符串中仅包含小写英文字母。

题解

出现$k$次的子串就是$SAM$上$right$集合大小为$k$的点。找出这些点然后用一个前缀和覆盖就可以求出哪个长度出现次数最多了。

代码

 1 #include<bits/stdc++.h>
 2 #define N (200009)
 3 #define LL long long
 4 using namespace std;
 5 
 6 char s[N];
 7 int n,k,sum[N];
 8 
 9 struct SAM {
10     int p,q,np,nq,last,cnt;
11     int son[N][28],fa[N],step[N],right[N];
12     int wt[N],od[N];
13     SAM() {last=cnt=1;}
14     
15     void Clear() {
16         p=q=np=nq=0;
17         last=cnt=1;
18         memset(son,0,sizeof(son));
19         memset(fa,0,sizeof(fa));
20         memset(step,0,sizeof(step));
21         memset(right,0,sizeof(right));
22         memset(wt,0,sizeof(wt));
23         memset(od,0,sizeof(od));
24     }
25     void Insert(int x) {
26         p=last; np=last=++cnt; step[np]=step[p]+1; right[np]=1;
27         while(!son[p][x] && p) son[p][x]=np, p=fa[p];
28         if (!p) fa[np]=1;
29         else {
30             q=son[p][x];
31             if (step[q]==step[p]+1) fa[np]=q;
32             else {
33                 nq=++cnt; step[nq]=step[p]+1;
34                 memcpy(son[nq],son[q],sizeof(son[q]));
35                 fa[nq]=fa[q]; fa[q]=fa[np]=nq;
36                 while (son[p][x]==q) son[p][x]=nq, p=fa[p];
37             }
38         }
39     }
40     void Init() {
41         int len=strlen(s);
42         for (int i=1; i<=cnt; ++i) wt[step[i]]++;
43         for (int i=1; i<=len; ++i) wt[i]+=wt[i-1];
44         for (int i=cnt; i>=1; --i) od[wt[step[i]]--]=i;
45         for (int i=cnt; i>=1; --i) right[fa[od[i]]]+=right[od[i]];
46         for (int i=1; i<=cnt; ++i) {
47             if (right[i]!=k) continue;
48             sum[step[i]+1]--; sum[step[fa[i]]+1]++;
49         }
50         int ans=0,cnt=0;
51         for (int i=1; i<=n; ++i) sum[i]+=sum[i-1];
52         for (int i=n; i>=1; --i) if (sum[i]>cnt) cnt=sum[i], ans=i;
53         cout<<(ans==0?-1:ans)<<endl;
54     }
55 }SAM;
56 
57 int main() {
58     int T;
59     cin>>T;
60     while (T--) {
61         memset(sum,0,sizeof(sum));
62         SAM.Clear();
63         cin>>s>>k;
64         n=strlen(s);
65         for (int i=0; i<n; ++i) SAM.Insert(s[i]-'a');
66         SAM.Init();
67     }
68 }

 

posted @ 2021-07-18 16:16  Refun  阅读(81)  评论(0编辑  收藏  举报