日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

美团杯2020 - 半前缀计数(后缀自动机)

發布時間:2024/4/11 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 美团杯2020 - 半前缀计数(后缀自动机) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:

蒜斜剛來PKU的時候還不知道有“北大算協”這個社團,因此他總是覺得周圍的人在偷偷議論著他,比如說:

“算協(注:非蒜斜)舉辦的活動好有趣啊!”

“算協(注:非蒜斜)好帥啊!”

蒜斜每次聽到這些話就會想入非非,但仔細想想,自己好像也沒有那么帥吧?最離譜的一次還是:

“算協(注:非蒜斜)有好多小哥哥”(霧)

自從蒜斜學習了半前綴之后,他把這些話都看開了 —— 對他來說,只要這些話里有 “蒜斜好” 的半前綴就足夠了。

題目描述

設小寫字母字符串?s, 長度為?n,?s[l:r] 表示第?l?個到第?r?個字符構成的子串,?l>r 時對應空串。

定義半前綴是?s[1:i]+s[j:k], 其中?0≤i<len(s),i<j≤len(s),j?1≤k≤len(s)。直觀上來說,你可以把半前綴理解成某一個前綴?s[1:k]刪除掉某一個子串后形成的結果(當然也允許不刪)。

給出字符串?ss,你需要求出?ss?的所有半前綴中,有多少個不同的字符串。

輸入格式

輸入一行包含一個小寫字符串?s(1≤|s|≤106)。

輸出格式

輸出一行一個整數,表示答案。

樣例一

input

aab

output

6

explanation

字符串?aab?的半前綴有:空串,a,b,aa,ab,aab。

樣例二

input

pkusaamtcup

output

217

限制與約定

Small Task:?|s|≤3000。

Large Task:?|s|≤106。

時間限制:2s

空間限制:512MB

題目分析:讀完題后第一反應就是后綴自動機,但是需要猜結論或者變形,然后就止步于此了

賽后看了jls的講題視頻豁然開朗,感覺這個題目太妙了

首先因為題目規定的半前綴是一個前綴與一個子串拼接而成,對于每個半前綴而言,肯定有很多的拼接方法,如果不加以約束的話,很容易重復統計,對于每個半前綴 s[ 0 : i ] + s[ j : k ] 我們只計算 i 最大的那個半前綴這樣就能做到不重不漏了

該如何確定某個半前綴已經是 i 最大的呢,一個必要條件是 s[ i + 1 ] != s[ j ] ,這個比較容易看出來,因為如果 s[ i + 1 ] == s[ j ] 的話,那么顯然 s[ 0 : i + 1 ] + s[ j + 1 : k ] 是一個比 i 更靠后的半前綴,所以必要性成立

接下來證明一下充分性,假設有更靠后的一個半前綴 s[ 0 : i' ] + s[ j' : k' ] 滿足 i < i' ,因為?s[ 0 : i ] + s[ j : k ] ==?s[ 0 : i' ] + s[ j' : k' ] ,所以 s[ i + 1 : i' ] == s[ j?: j?+ [一段區間] ] ,即 s[ i + 1 ] == s[ j ] ,與上面的結論相悖,所以證明了充分性

綜上,s[ i + 1 ] != s[ j ] 是?s[ 0 : i ] + s[ j : k ] 作為最后一次出現的半前綴的重分必要條件

那么接下來我們只需要枚舉 i ,然后統計 s[ i + 1 : k ] 中有多少個首字母不為 s[ i + 1 ] 的本質不同子串即可

然后用后綴自動機解決就好了,因為要統計的區間實際上為 [ i + 1 , n ] ,所以我們可以倒著插入字符串,因為字符串在翻轉之后,本質不同的子串個數是不變的,每次插入后利用 last 指針更新 cnt 數組,cnt[ j ] 表示截止到目前為止,首字母為 j 的本質不同子串有多少個,將答案累加就好了

最后需要注意的是,初始化為賦值為 n + 1 ,這里的 1 是指空串,而 n 是指形如 [ 1 , 1 ] + 空串,[ 1 , 2 ] + 空串, [ 1 , 3 ] + 空串... + ?[ 1 , n ] + 空串 ,這樣的半前綴,因為后綴自動機無法統計空串的個數,所以需要我們自己手動計算上空串的貢獻

代碼:

#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;char s[N];LL cnt[26];int tot=1,last=1;struct Node {int ch[26];int fa,len; }st[N<<1];void add(int x) {int p=last,np=last=++tot;st[np].len=st[p].len+1;while(p&&!st[p].ch[x])st[p].ch[x]=np,p=st[p].fa;if(!p)st[np].fa=1;else{int q=st[p].ch[x];if(st[p].len+1==st[q].len)st[np].fa=q;else{int nq=++tot;st[nq]=st[q]; st[nq].len=st[p].len+1;st[q].fa=st[np].fa=nq;while(p&&st[p].ch[x]==q)st[p].ch[x]=nq,p=st[p].fa;//向上把所有q都替換成nq}} }int main() { #ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); // freopen("output.txt","w",stdout); #endif // ios::sync_with_stdio(false);scanf("%s",s);int n=strlen(s);LL ans=n+1;for(int i=n-1;i>=0;i--){add(s[i]-'a');cnt[s[i]-'a']+=st[last].len-st[st[last].fa].len;for(int j=0;j<26;j++)if(s[i]-'a'!=j)ans+=cnt[j];}printf("%lld\n",ans);return 0; }

?

總結

以上是生活随笔為你收集整理的美团杯2020 - 半前缀计数(后缀自动机)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。