給定乙個只包含小寫字母的字串\(s\),
請你求出 \(s\) 的所有出現次數不為 \(1\) 的子串的出現次數乘上該子串長度的最大值。
\(|s|\leq 10^6\)。
\[\mathrm(t)=\\cdots s_x=t\}
\]對於 \(\mathrm(t)\) 相同的所有子串 \(t\),我們把他們歸到乙個等價類中,然後對於每乙個等價類建立乙個節點。
如果 \(\mathrm(t)⫋\mathrm(t')\),且不存在任意 \(t''(|t''|>|t'|)\) 滿足 \(\mathrm(t)⫋\mathrm(t'')\),那麼我們就從 \(t'\) 所在類的節點向 \(t\) 所在類的節點連一條邊。
容易發現,連邊結束後,所有的點構成了一棵樹,我們稱其為 parent 樹。可以證明樹上的點數是 \(o(n)\) 的。
我們記 \(\mathrm(x)\) 表示節點 \(x\) 所在的等價類中,長度最長的子串的長度,\(\mathrm(x)\) 表示最短長度。那麼容易發現 \(\mathrm(x)=\mathrm(fa_x)+1\)。因為顯然在原串中它們是包含關係且只差乙個字元。
乙個字尾自動機(sam)的節點和 parent 樹完全一致,但是連邊方式不同。在 sam 中,乙個類所對應的節點會向另乙個類連一條有向邊,當且僅當在這個類的任意子串的末尾新增乙個字元 \(c\),得到的串的 \(\mathrm\) 集合等於後者所在的類的 \(\mathrm\) 集合。那麼這條有向邊的權值就為這個字元 \(c\)。
依舊是可以證明,sam 的邊數是 \(o(n)\) 的。具體可以看上面推薦的文章。wtcl。
然後利用 parent 樹和字尾自動機,就可以解決字串的很多問題。但是為了保證複雜度,如果需要排序,那麼要採用基數排序。
回到本題,建出 parent 樹後,容易發現乙個點所表示的等價類中,所有子串出現的次數都等於 parent 樹上這個節點的子樹大小。
那麼直接求出每乙個節點的大小,乘上 \(\mathrm\) 取最大值即可。
時間複雜度 \(o(n)\)。
#include using namespace std;
typedef long long ll;
const int n=2000010;
int n,a[n],cnt[n];
ll ans,f[n];
char s[n];
struct sam
void ins(int c)
} }
void topsort() }
}sam;
int main()
洛谷P3804 模板 字尾自動機
題意 求字串 s 中所有出現次數不為 1 的子串的出現次數乘上該子串長度的最大值。方法 建立sam,令葉子節點 size 等於 1。按照拓撲序從下往上計數。個數大於1的就累計結果。include include include using namespace std define n 1000010...
洛谷P3804 模板 字尾自動機
題目大意 給乙個只包含小寫字母的字串 s 請求出 s 的所有出現次數不為 1 的子串的出現次數乘上該子串長度的最大值。題解 用字尾自動機求出每個字串的長度,排序後求出字串出現次數 字尾自動機是背板的。卡點 1.rnk 陣列沒有開兩倍 板背錯c code include include define ...
洛谷 P3804 字尾自動機
給定乙個只包含小寫字母的字串ss 請你求出 ss 的所有出現次數不為 11 的子串的出現次數乘上該子串長度的最大值。輸入格式 一行乙個僅包含小寫字母的字串ss 輸出格式 乙個整數,為 所求答案 輸入樣例 1 abab 輸出樣例 1 4 對於10 10 的資料,s 1000 s 1000 對於100 ...