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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

[NOI2018]你的名字

發布時間:2024/4/13 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [NOI2018]你的名字 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

SAM寫的太不熟練了~~SAM上的線段樹合并也不熟練~~~

調了半天樣例

題目大意:

給定一個S,Q次詢問,每次給出T,l,r,

求對于S[l,r],屬于T的子串卻不屬于S[l,r]的子串有多少個

看上去挺簡潔的一個問題。。。

暴力68pts

對于S[1,n]68pts?

如果做過

?[HEOI2015]最短不公共子串

就好做多了!

可以對A,B分別建SAM

拓撲排序找到A中每個點的后面路徑條數。

然后在A上面匹配一遍,如果B匹配不出,直接加上A后面的路徑條數

100pts?

剛才的暴力方法實際上不適用了

因為DAG根本無法精確找到[l,r]的部分。。

?

換一個角度

不從圖的路徑角度考慮子串了

直接從子串定義考慮

?

考慮,對于T,[1,i]這個前綴貢獻的答案

假設同一個子串可以算多次的話

把[1,i]這個前綴在S[l,r]中匹配,設最長長度是mx

那么貢獻的答案就是i-mx

?

怎么計算"把[1,i]這個前綴在S[l,r]中匹配"得到的最長后綴長度?

用線段樹合并維護S的SAM中,點P的right集合

設[1,i-1]匹配的長度為now,匹配在SAM上的點為p

如果p有c出點,出點是x

如果x的right集合中有[l+now,r]區間中一個元素,意味著可以直接匹配下去,得到最長的長度了。break

否則now--,繼續嘗試。如果now==len[fa[p]],可以更新到更大的集合了,p=fa[p]

設i前綴匹配長度為lim[i]

?upda:2019.3.8:

這個匹配本質上是不斷找到當前可能的最長后綴now+'c'在S中所有出現位置,然后看這些出現位置有沒有末尾在[l+now,r]的

?

至于相同的子串是1個

那么對T串再建立SAM,用parent樹去重,parent樹上dfs,每個點的貢獻是max(0,min(len[x]-len[fa[x]],len[x]-lim[x]))

相當于把同構的串放在一起,只計算一次

代碼

注意,

1.線段樹合并還要支持之后的查詢

所以必須每次新建節點

類似:CF666E Forensic Examination

2.tot,cnt,num計數器很多別混(懶得namespace了)

#include<bits/stdc++.h> #define reg register int #define il inline #define mid ((l+r)>>1) #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x); } namespace Miracle{ const int N=1e6+5; const int M=1e6+5; int n,q; char s[N]; int lim[M]; ll ans; struct SAMSAM{int ch[N][26];int len[N],nd,fa[N];int cnt;void init(){cnt=1,nd=1;}struct tr{int ls,rs;int sum;}t[N*20];int rt[N];int tot;void pushup(int x){t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum;}void upda(int &x,int l,int r,int to){x=++tot;if(l==r) {t[x].sum=1;return;}if(to<=mid) upda(t[x].ls,l,mid,to);else upda(t[x].rs,mid+1,r,to);pushup(x);}int merge(int x,int y,int l,int r){// cout<<" merging "<<x<<" "<<y<<" :: "<<l<<" "<<r<<endl;if(!x||!y) return x+y;int id=++tot;if(l==r){t[id].sum=t[y].sum+t[x].sum;return id;}t[id].ls=merge(t[x].ls,t[y].ls,l,mid);t[id].rs=merge(t[x].rs,t[y].rs,mid+1,r);pushup(id);return id;}void ins(int c,int l){int p=nd;len[nd=++cnt]=l;upda(rt[cnt],1,n,l);for(;p&&ch[p][c]==0;p=fa[p]) ch[p][c]=cnt;// cout<<"pp "<<p<<" cnt "<<cnt<<" char "<<c<<" ll "<<l<<" : "<<ch[1][c]<<endl;if(!p){fa[cnt]=1;return;}int q=ch[p][c];if(len[q]==len[p]+1){fa[cnt]=q;return;}len[++cnt]=len[p]+1;fa[cnt]=fa[q];fa[q]=fa[nd]=cnt;for(reg j=0;j<26;++j) ch[cnt][j]=ch[q][j];for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=cnt;}struct edge{int nxt,to;}e[2*N];int hd[2*N],num;void add(int x,int y){e[++num].nxt=hd[x];e[num].to=y;hd[x]=num;}void build(){// cout<<" cnt "<<cnt<<endl;for(reg i=2;i<=cnt;++i){// cout<<i<<" : fafa "<<fa[i]<<endl; add(fa[i],i);}}void dfs(int x){// cout<<" xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "<<x<<" rt "<<rt[x]<<" sz "<<t[x].sum<<endl;for(reg i=hd[x];i;i=e[i].nxt){int y=e[i].to;dfs(y);rt[x]=merge(rt[x],rt[y],1,n);}}int query(int x,int l,int r,int L,int R){// cout<<" xx "<<x<<" "<<l<<" and "<<r<<" : query "<<L<<" "<<R<<" sz "<<t[x].sum<<endl;if(l>r) return 0;if(!x) return 0;if(L<=l&&r<=R) return t[x].sum;int ret=0;if(L<=mid) ret+=query(t[x].ls,l,mid,L,R);if(mid<R) ret+=query(t[x].rs,mid+1,r,L,R);return ret; } }SAM; struct samsam{int ch[M][26];int len[M],nd,fa[M];int mx[M];int cnt;void init(){cnt=1,nd=1;}void ins(int c,int l){int p=nd;len[nd=++cnt]=l;mx[cnt]=lim[l];//warning!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!for(;p&&ch[p][c]==0;p=fa[p]) ch[p][c]=cnt;if(!p){fa[cnt]=1;return;}int q=ch[p][c];if(len[q]==len[p]+1){fa[cnt]=q;return;}len[++cnt]=len[p]+1;fa[cnt]=fa[q];fa[q]=fa[nd]=cnt;for(reg j=0;j<26;++j) ch[cnt][j]=ch[q][j];for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=cnt;}struct edge{int nxt,to;}e[2*N];int hd[2*N],num;void add(int x,int y){e[++num].nxt=hd[x];e[num].to=y;hd[x]=num;}void build(){for(reg i=2;i<=cnt;++i){add(fa[i],i);}}void dfs(int x){////ans ansnsnannsansansna asn ansfor(reg i=hd[x];i;i=e[i].nxt){int y=e[i].to;dfs(y);mx[x]=max(mx[x],mx[y]);}ans+=max(0,min(len[x]-mx[x],len[x]-len[fa[x]]));}void clear(){for(reg i=1;i<=cnt;++i){for(reg j=0;j<26;++j){ch[i][j]=0;}mx[i]=0;len[i]=0;hd[i]=0;fa[i]=0;}num=0;cnt=1;} }sam; void clear(){sam.clear();ans=0; } int main(){scanf("%s",s+1);n=strlen(s+1);SAM.init();for(reg i=1;i<=n;++i){SAM.ins(s[i]-'a',i);} // cout<<" after ins "<<endl; SAM.build(); // cout<<" after build "<<endl;SAM.dfs(1); // cout<<" after dfs "<<endl; rd(q);int l,r;while(q--){clear();scanf("%s",s+1);rd(l);rd(r);int len=strlen(s+1);int now=0,p=1;for(reg i=1;i<=len;++i){//pipei int c=s[i]-'a';while(1){// cout<<" cc "<<c<<" "<<SAM.ch[p][c]<<endl;if(SAM.ch[p][c]&&SAM.query(SAM.rt[SAM.ch[p][c]],1,n,l+now,r)){++now;p=SAM.ch[p][c];break;} if(!now) break;--now;if(now==SAM.len[SAM.fa[p]]) p=SAM.fa[p];}lim[i]=now;// cout<<" lim "<<i<<" : "<<lim[i]<<" p "<<p<<endl; }sam.init();for(reg i=1;i<=len;++i){//insertsam.ins(s[i]-'a',i);}ans=0;sam.build();sam.dfs(1);printf("%lld\n",ans);}return 0; }} signed main(){Miracle::main();return 0; }/*Author: *Miracle*Date: 2019/1/18 17:48:14 */

總結:

SAM對于公共子串問題一個基本的方法是跑上去匹配

然后下來再考慮每個位置的貢獻

parent樹、DAG圖無形對子串進行了同構的去重

?

轉載于:https://www.cnblogs.com/Miracevin/p/10289785.html

總結

以上是生活随笔為你收集整理的[NOI2018]你的名字的全部內容,希望文章能夠幫你解決所遇到的問題。

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