為了提高智商,zjy開始學習弦論。這一天,她在《 string theory》中看到了這樣一道問題:對於乙個給定的長度為n的字串,求出它的第k小子串是什麼。你能幫幫她嗎?
第一行是乙個僅由小寫英文本母構成的字串s
第二行為兩個整數t和k,t為0則表示不同位置的相同子串算作乙個,t為1則表示不同位置的相同子串算作多個。k的意義見題目描述。
輸出資料僅有一行,該行有乙個字串,為第k小的子串。若子串數目不足k個,則輸出-1。
輸入 #1複製
aabc輸出 #1複製0 3
aab輸入 #2複製
aabc輸出 #2複製1 3
aa輸入 #3複製
aabc輸出 #3複製1 11
-1對於10%的資料,n ≤ 1000。
對於50%的資料,t = 0。
對於100%的資料,n ≤ 5 × 10^5, t < 2, k ≤ 10^9。
利用字尾自動機求解
endpos 大小就是 有多少個這樣的子串
按照parent 反拓撲序 求 sum [ i ] (經過自動機每個節點的子串數目)
關於反拓撲序的解釋
將所有的parent 反過來,我們就得到了parent樹
如果要處理什麼,就需要parent樹的拓撲序
(因為parent相當於包含了所有的他的子樹,都需要更新上去)
其實不需要拓撲排序
我們知道s 的endpos完全被parent 的endpos包含
s.longest一定長於parent.longest
所以,乙個狀態的longest越長,它一定要被更先訪問
所以,按照longest 的長度進行桶排序就可以解決拓撲序了
求出來之後按照字典序遍歷即可
參考
#include#include#include#includeusing namespace std;
typedef long long ll;
const int maxn=5e5+5;
struct sam
void extend(int c)
if(!p)
slink[np]=root;
else
}else
slink[np]=q;
}last=np;
endpos[np]=1;
}void build(string s)
}}sam;
ll sum[maxn<<1];
void f(int x,int k)
else}}
}int main()
}else if(t==1)
}sum[1]=0;
sam.endpos[1]=0;
for(int i=sam.now;i>=sam.root;i--)
}if(sum[sam.root]printf("-1");
else
f(1,k);
return 0;
}
洛谷P3975 TJOI2015 弦論
題目大意 求乙個字串的第 k 大字串,t 表示長得一樣位置不同的字串是否算多個 題解 sam 先求出每個位置可以到達多少個字串 right 陣列 然後在轉移圖上 dp 若 t 1 初始值賦成 right 陣列大小,否則賦成 1 卡點 無 c code include include include ...
洛谷P3975 弦論
題意 求乙個串的字典序第k小的子串 本質不同第k小的子串。解 一開始我的想法是在字尾樹上找,但是不知道字尾樹上的邊對應的是哪些字元.然而可以不用fail樹轉移,用轉移邊轉移即可。先建乙個字尾自動機,記憶化搜尋每個節點向後向後有多少個串。然後從起點開始向後乙個字元乙個字元的確定。注意每到乙個新點就要判...
TJOI2015 弦論(第k小子串)
題意 對於乙個給定的長度為n的字串,求出它的第k小子串。有引數t,t為0則表示不同位置的相同子串算作乙個,t為1則表示不同位置的相同子串算作多個。題解 首先,因為t的原因,字尾陣列較難實現,這裡不討論。使用字尾自動機 因為,這裡需要按字典序考慮子串,所以要使用trs指標。首先,計算出每個子串的貢獻 ...