1. 程式人生 > 其它 >P5341 [TJOI2019]甲苯先生和大中鋒的字串

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 }