51nod 1600 Simplr KMP(后缀自动机+维护树上的数据结构)
生活随笔
收集整理的這篇文章主要介紹了
51nod 1600 Simplr KMP(后缀自动机+维护树上的数据结构)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題意:對于每個位置,統計有多少個相同的字串。
分析:按照題目的意思,把fail樹畫出來就會發現,對于第i個字符:ans[i] = ans[i-1] + (ans[i-1]-ans[i-1]) + cal(i);
?? cal(i)是計算s[1…i-1]所有子串與s[1…i]的最長公共后綴的和。換句話說,根據后綴自動機性質,沿著parent樹往上走可以知道對于后綴s[1…i]的所有位置的公共后綴長度以及個數(right集合的大小)。很容易可以計算出cal(i), 只要每次新增一個字符的時候,在parent樹上往根走,邊走邊統計 val[i]*(right[pre[i]]-right[i])。由于博主我是在建后綴自動機的同時計算ans,所以parent樹是動態地邊的,所以博主用了LCT去維護。
吐槽:看問題的角度果然可以有好多,顯然下面代碼能折射出我看問題的角度顯然不太好。為了這道題,苦苦地學了LCT,然而后來看別人博客發現可以直接用樹鏈剖分寫的。
/************************************************ Author :DarkTong Created Time :2016/9/6 22:58:31 File Name :51nod_simpleKMP.cpp *************************************************/#include <bits/stdc++.h> using namespace std; typedef unsigned long long ULL; typedef long long LL; const int INF = 0x3f3f3f3f; const double eps = 1e-9; const int MOD = 1e9+7; const int maxn = 2*100000 + 100; LL ans[maxn]; struct Splay_Tree {int ch[maxn][2], pre[maxn], dpre[maxn];// dpre用于樹-樹連接// pre用于樹內連接int lazy[maxn], mval[maxn], mr[maxn];LL sum[maxn];int go[maxn][26], right[maxn], val[maxn];int sz, last, root;void pushdown(int x){int l = ch[x][0], r = ch[x][1];if(l)lazy[l] += lazy[x], right[l] += lazy[x], mr[l] += lazy[x];if(r)lazy[r] += lazy[x], right[r] += lazy[x], mr[r] += lazy[x];lazy[x] = 0;mr[x] = max(right[x], max(mr[l], mr[r]));}void pushup(int x){sum[x] = (sum[ch[x][0]] + sum[ch[x][1]] + val[x]*mval[x])%MOD;mr[x] = max(right[x], max(mr[ch[x][0]], mr[ch[x][1]]));//ch[x][1] 不是 x的兒子,因為x不是根。 }//旋轉操作(ok),請注意:對于被旋轉的元素,其dpre=0,也就是說是對輔助樹在操作,不影響到原樹。void rotate(int x){int y = pre[x], d = (ch[y][1]==x);ch[y][d] = ch[x][!d]; pre[ch[x][!d]] = y; pre[x] = pre[y]; pre[y] = x;ch[x][!d] = y;if(dpre[y]) dpre[y]=0, dpre[x]=1;else ch[pre[x]][ch[pre[x]][1]==y] = x;pushup(y);// pushup(x); }//直接訪問節點o,并將其選轉至根部void P(int x){if(!dpre[x]) P(pre[x]); //找重鏈的根,然后把標志向下傳一遍 pushdown(x);}void splay(int x){P(x);while(!dpre[x]){int f = pre[x], ff = pre[f];if(dpre[f]) rotate(x);else if( (ch[ff][1]==f) == (ch[f][1]==x) ) rotate(f), rotate(x);else rotate(x), rotate(x);}pushup(x);}//AuxiliaryTree鏈接操作(數據更新)void Access(int x){int y = 0;for(;x;y=x, x=pre[x]){splay(x); //為了去掉元重兒子,旋轉后兒子在其左子樹dpre[ch[x][1]] = 1; //去掉元重兒子ch[x][1] = y;dpre[y] = 0; //加入新的重兒子vmval[x] = right[x] - mr[ch[x][1]];pushup(x);}}//加邊,連接兩棵樹,u->v;void link(int u, int v){Access(u);pre[u]=v;dpre[u] = 1;Access(u);}void cut(int x){Access(x); splay(x);int l = ch[x][0];pre[l] = 0; dpre[l] = 1;ch[x][0] = 0;Access(x);}int fa(int u){Access(u);splay(u);u = ch[u][0];while(ch[u][1]) u = ch[u][1];return u;}void SAM(){last = root = sz = 1;}void ini(int sz, int v){val[sz] = v;mval[sz] = 0;ch[sz][0]=ch[sz][1]=0;dpre[sz] = 1;pre[sz]=sum[sz]=lazy[sz]=right[sz]=0;mr[sz]=0;memset(go[sz], 0, sizeof go[sz]);}void extend(int w, int th){int p = last;int np = ++sz; ini(sz, val[p]+1); //葉子節點while(p && go[p][w] == 0) go[p][w] = np, p = fa(p);if(p == 0) link(np, root);else{int q = go[p][w];if(val[p] + 1 == val[q]) link(np, q);else{int nq = ++sz; ini(sz, val[p]+1); //非葉子節點memcpy(go[nq], go[q], sizeof go[q]);mr[nq] = right[nq] = right[q]; mval[nq] = right[q]; int faq = fa(q);cut(q); link(q, nq); link(np, nq); link(nq, faq); while(p && go[p][w] == q) go[p][w] = nq, p = fa(p);}}last = np;ans[th] = cal(np);}LL cal(int x){x = fa(x);Access(x);splay(x);//updatelazy[x]++;right[x]++;mval[x] = right[x];pushup(x);return sum[x];} }slt; char s[maxn];int main() { // freopen("out.txt", "r", stdin); // freopen("xx1.txt", "w", stdout);int T, cas=1, n;scanf("%d%s", &n, s);slt.SAM();slt.ini(1, 0);for(int i=0;i<n;++i){slt.extend(s[i]-'a', i);} // for(int i=0;i<n;++i) cout<<ans[i]<<" "; cout<<endl;for(int i=2;i<n;++i) ans[i] = (2*ans[i-1]-ans[i-2]+ans[i]+MOD)%MOD;for(int i=0;i<n;++i) printf("%lld\n", ans[i]);return 0; }轉載于:https://www.cnblogs.com/DarkTong/p/5851772.html
總結
以上是生活随笔為你收集整理的51nod 1600 Simplr KMP(后缀自动机+维护树上的数据结构)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转: 三大WEB服务器对比分析(apa
- 下一篇: jquery toggle()设置