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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

4566: [Haoi2016]找相同字符 SAM

發(fā)布時間:2023/11/27 生活经验 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 4566: [Haoi2016]找相同字符 SAM 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

折騰了好久。不過收獲還是很多的。第一次自己去畫SAM所建出來fail樹。深入體會了這棵樹的神奇性質(zhì)。

當(dāng)然,我最終靠著自己A掉了。(這是我第一次推SAM的性質(zhì)(以前都是抄別人的,感覺自己好可恥),不過感覺好像是摸著黑行走啊!)

這道題,可以先對第一個串建出后綴自動機(jī)。然后第二個串在后綴自動機(jī)上跑。

首先,SAM所建出的fail樹的性質(zhì)有:

1: 樹上一個節(jié)點(diǎn)對應(yīng)了多個串,串的個數(shù)是 len[x] - len[fa[x]], 同時它們都出現(xiàn)了 sz[x] 次(感覺好像說不大清,可以看一下代碼對于sz數(shù)組的處理)。

2: 設(shè)串的長度為 l 那么每個節(jié)點(diǎn) x 的 sz[x] * (len[x] - len[fa[x]]) 的和為 (l+1) * l / 2

3: ? 樹上一個節(jié)點(diǎn)的所有后代所代表的串都是以這個節(jié)點(diǎn)代表的串為后綴的串。

4: ? 根據(jù)性質(zhì)3推出: 如果一個字符串匹配到一個樹上的某個節(jié)點(diǎn), 那么這個節(jié)點(diǎn)的所有祖先所代表的所有祖先都是它的子串,但那個節(jié)點(diǎn)本身所代表的所以串并不一定都是他的子串。

?

所以這道題的ans應(yīng)該呼之欲出了。

(說了這么多,你們知道解題的思路是什么嗎?顯然對于子串的問題,我們肯定是假設(shè)當(dāng)前匹配到第i個字符的時候,把以第i個字符結(jié)尾的所有字串都處理掉。)?

那么假設(shè)當(dāng)前匹配到 i 字符時轉(zhuǎn)移到了節(jié)點(diǎn)x, 那么x對答案的貢獻(xiàn)就是 祖先所代表的所有的串(可以預(yù)處理)和 ?匹配到 i 字符時,(第二個串與第一個串的最長匹配長度-len[fa[x]]?)*sz[x]?

第二個串與第一個串的最長匹配長度-len[fa[x]](這個就是第二個串在x節(jié)點(diǎn)內(nèi)所能匹配的子串?dāng)?shù))

?

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define rep(i,j,k) for(register int i = j; i <= k; i++)
 5 #define dow(i,j,k) for(register int i = j; i >= k; i--)
 6 #define ez(i,j) for(edge*i = head[j]; i; i=i->next)
 7 #define maxn 200005
 8 #define ll long long
 9 using namespace std;
10 
11 struct edge{ int to; edge*next; } e[maxn<<1], *pt = e, *head[maxn<<1];
12 inline void add(int x,int y) { pt->to = y, pt->next = head[x], head[x] = pt++; }
13 
14 int last = 1, u, v, nv, p, tot = 1, sz[maxn<<1], dep[maxn<<1], fa[maxn<<1], son[maxn<<1][26];
15 inline void extend(int c) {
16     u = last; p = last = ++tot; dep[p] = dep[u] + 1; sz[p] = 1;
17     while( u && !son[u][c] ) son[u][c] = p, u = fa[u];
18     if( !u ) fa[p] = 1;
19     else {
20         v = son[u][c];
21         if( dep[v] == dep[u] + 1 ) fa[p] = v;
22         else {
23             nv = ++tot; dep[nv] = dep[u] + 1;
24             fa[nv] = fa[v]; fa[v] = fa[p] = nv;
25             memcpy(son[nv],son[v],sizeof(son[v]));
26             while( son[u][c] == v ) son[u][c] = nv, u = fa[u]; 
27         }
28     } 
29 }
30 
31 int q[maxn<<1], tong[maxn<<1];
32 inline void pre() {
33     rep(i,1,tot) tong[dep[i]]++;
34     rep(i,1,tot) tong[i] += tong[i-1];
35     dow(i,tot,1) q[tong[dep[i]]--] = i;
36     int x;
37     dow(i,tot,1) x = q[i], sz[fa[x]] += sz[x];
38 
39 }
40 char c1[maxn]; ll dp[maxn<<1];
41 #define to i->to
42 inline void dfs(int x) {
43     dp[x] += 1ll * sz[x] * (dep[x] - dep[fa[x]]);
44     ez(i,x) dp[to] += dp[x], dfs(to);
45 }
46 
47 int main() {
48     scanf("%s", c1); int s1 = strlen(c1);
49     rep(i,0,s1-1) extend(c1[i]-'a');  
50     pre(); rep(i,1,tot) if( fa[i] ) add(fa[i],i); dfs(1);
51     //rep(i,1,tot) { rep(j,0,5) cout<<son[i][j]<<" "; cout<<endl; }
52     //rep(i,1,tot) cout<<sz[i]<<" "<<dp[i]<<" "<<dep[i]-dep[fa[i]]<<" "<<fa[i]<<endl;
53     scanf("%s", c1); s1 = strlen(c1);
54     int t, p = 1, l = 0; ll ans = 0;
55     rep(i,0,s1-1) {
56         t = c1[i] - 'a';
57         while( p && !son[p][t] ) p = fa[p];
58         if( !p ) p = 1, l = 0;
59         else l = min(l,dep[p]) + 1, p = son[p][t];
60         ans += dp[fa[p]], ans += 1ll * (l - dep[fa[p]]) * sz[p];
61     }
62     cout<<ans<<endl;
63     return 0;
64 }

?

轉(zhuǎn)載于:https://www.cnblogs.com/83131yyl/p/5483535.html

總結(jié)

以上是生活随笔為你收集整理的4566: [Haoi2016]找相同字符 SAM的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。