CF653F Paper task
求給出的括號序列本質不同的合法括號子串個數,
對每種前綴和分開統計的做法很好想。二分,區間最小值滿足條件(單調棧),去重就後綴數組調整二分範圍。詳見 blog
後綴自動機也差不多,對每個節點找任意一個endpos,同樣限制出一個範圍。詳見 blog2
CF587F Duff is Mad
統計多串在某串中的出現次數,容易想到AC自動機。
在 中的出現次數相當於是在 樹上對 對應結束節點的子樹權值 +1 後 的所有節點的權值和。
差分之後,相當於求一個前綴與 的答案。
對於 ,可以暴力遍歷 的所有節點,離線掃描 子樹加,單點查。
對於 ,總的串個數不超過 ,可以 處理所有與之有關的詢問:預處理 樹上每個點的子樹中有多少 的節點,然後離線掃描 加入和。
詳細的複雜度分析見 xht37’s blog
CF914F Substrings in a String
分塊,大小爲 ,每個塊建後綴自動機,有修改就重構對應塊。
查詢 ,整塊塊內就在自動機上走,答案爲endpos的個數。跨越塊或者散塊暴力KMP匹配。
可以 bitset 暴力匹配,存每個位置匹配到第 位是否仍然合法,然後繼續加入 位。最後差分一下。
Code:
#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
int n,m;
char s[maxn],t[maxn];
bitset<maxn>a[26],ans;
int main()
{
scanf("%s",s+1),n=strlen(s+1);
for(int i=1;i<=n;i++) a[s[i]-='a'][i]=1;
scanf("%d",&m);
for(int op,x,y,c;m--;){
scanf("%d%d",&op,&x);
if(op==1){
getchar(),c=getchar()-'a';
a[s[x]][x]=0,a[s[x]=c][x]=1;
}
else{
scanf("%d%s",&y,t); int len=strlen(t);
ans.set();
for(int i=0;i<len;i++) ans&=a[t[i]-'a']>>i;
printf("%d\n",(y-x+1>=len)?(ans>>x).count()-(ans>>(y-len+2)).count():0);
}
}
}
CF741E Arpa’s abnormal DNA and Mehrdad’s deep interest
洛谷題解區的 Durant_Lee 的題解寫的很清晰了。
比較兩個位置的字典序分成五部分(比較三段),後綴數組LCP比較。(略麻煩)
然後就可以 sort 給所有位置排序求出排名,轉化爲求詢問區間裏的排名最小值。
對於 ,可以枚舉每一塊,然後求在區間 和 的最小值,一個全局RMQ即可。
對於 ,離線詢問,對每種 分開做,然後枚舉餘數 ,對所有 建立RMQ, 對於 的詢問求 的最小值即可。
Code:
#include<bits/stdc++.h>
#define maxn 200005
using namespace std;
const int S = 150;
int n,m,lg[maxn];
namespace SA{
int ary[4][maxn],b[maxn],st[18][maxn];
int *sa=ary[0],*rk=ary[1],*nsa=ary[2],*nrk=ary[3];
void build(int n,char *a){
for(int i=1;i<=n;i++) b[a[i]]++;
for(int i=1;i<=255;i++) b[i]+=b[i-1];
for(int i=1;i<=n;i++) sa[b[a[i]]--]=i;
for(int i=1;i<=n;i++) rk[sa[i]]=rk[sa[i-1]]+(a[sa[i-1]]!=a[sa[i]]);
for(int k=1;rk[sa[n]]<n;k<<=1){
for(int i=1;i<=n;i++) b[rk[sa[i]]]=i;
for(int i=n;i>=1;i--) if(sa[i]-k>0) nsa[b[rk[sa[i]-k]]--]=sa[i]-k;
for(int i=n-k+1;i<=n;i++) nsa[b[rk[i]]--]=i;
for(int i=1;i<=n;i++) nrk[nsa[i]]=nrk[nsa[i-1]]+(rk[nsa[i-1]]!=rk[nsa[i]]||rk[nsa[i-1]+k]!=rk[nsa[i]+k]);
swap(sa,nsa),swap(rk,nrk);
}
for(int i=1,j,k=0;i<=n;st[0][rk[i]]=k,i++)
for(k&&(k--),j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int j=1;j<=lg[n];j++)
for(int i=1,l=1<<j;i+l-1<=n;i++)
st[j][i]=min(st[j-1][i],st[j-1][i+(l>>1)]);
}
int LCP(int x,int y){
if(x==y) return maxn;
if((x=rk[x])>(y=rk[y])) swap(x,y); x++;
int k=lg[y-x+1];
return min(st[k][x],st[k][y-(1<<k)+1]);
}
int cmp(int x,int y,int l){return LCP(x,y)>=l?0:rk[x]<rk[y]?-1:1;}
bool cmp2(int x,int y){//equal return 1.
int f=x>y,t; if(f) swap(x,y);
if(x+m<=y){
if(t=cmp(n+1,x+1,m)) return (t<0)^f;
if(t=cmp(x+1,x+m+1,y-(x+m))) return (t<0)^f;
if(t=cmp(y+1-m,n+1,m)) return (t<0)^f;
}
else{
if(t=cmp(n+1,x+1,y-x)) return (t<0)^f;
if(t=cmp(n+1+y-x,n+1,x+m-y)) return (t<0)^f;
if(t=cmp(x+1,n+1+x+m-y,y-x)) return (t<0)^f;
}
return 1^f;
}
}
int Q,RK[maxn],PS[maxn],ans[maxn],mn[18][maxn];
char s1[maxn],s2[maxn];
struct node{
int l,r,k,x,y,id;
void init(int i){id=i,scanf("%d%d%d%d%d",&l,&r,&k,&x,&y);}
bool operator < (const node &p)const{return k<p.k;}
}q[maxn];
int RMQ(int x,int y){
if(x>y) return maxn;
int k=lg[y-x+1];
return min(mn[k][x],mn[k][y-(1<<k)+1]);
}
void Pre(int N){
for(int j=1;j<=lg[N+1];j++) for(int i=0,l=1<<j;i+l-1<=N;i++) mn[j][i]=min(mn[j-1][i],mn[j-1][i+(l>>1)]);
}
int main()
{
scanf("%s%s%d",s1+1,s2+1,&Q),n=strlen(s1+1),m=strlen(s2+1);
for(int i=1;i<=m;i++) s1[n+i]=s2[i];
SA::build(n+m,s1);
for(int i=0;i<=n;i++) PS[i]=i;
sort(PS,PS+n+1,SA::cmp2);
for(int i=0;i<=n;i++) RK[PS[i]]=i;
for(int i=1;i<=Q;i++) q[i].init(i),ans[i]=maxn;
for(int i=0;i<=n;i++) mn[0][i]=RK[i]; Pre(n);
for(int i=1;i<=Q;i++) if(q[i].k>S)
for(int j=0,k=q[i].k;j*k<=n;j++)
ans[i]=min(ans[i],RMQ(max(j*k+q[i].x,q[i].l),min(j*k+q[i].y,q[i].r)));
sort(q+1,q+1+Q);
for(int k=1,s=1,t;k<=S&&k<=n&&s<=Q;k++) if(q[s].k==k){
for(t=s;t<Q&&q[t+1].k==k;t++);
for(int r=0;r<k;r++){
int M = n/k-(n%k<r);
for(int i=0;i<=M;i++) mn[0][i]=RK[i*k+r]; Pre(M);
for(int i=s;i<=t;i++) if(q[i].x<=r&&r<=q[i].y)
ans[q[i].id]=min(ans[q[i].id],RMQ(q[i].l/k+(q[i].l%k>r),q[i].r/k-(q[i].r%k<r)));
}
s=t+1;
}
for(int i=1;i<=Q;i++) printf("%d%c",ans[i]<maxn?PS[ans[i]]:-1,i==Q?10:32);
}
CF862F Mahmoud and Ehab and the final stage
區間LCP是相鄰兩兩LCP的最小值。
對 LCP <= K 的每種LCP開一棵線段樹,如果某個位置>=LCP就爲1,否則爲0,查詢答案就是 (區間最大子段和+1) * LCP。
對 LCP > K,串的個數不超過 總長/K,把這些串拿出來建笛卡爾樹,dfs求答案。可以用 vector 存這些位置,修改的時候暴力插入。
取 時複雜度 ,實際 取大一點。
[CTSC2010]珠寶商
暴力的做法是從一個樹上節點開始走,在S的後綴自動機中匹配,統計endpos大小的和。
涉及路徑問題,通常的想法是點分治。
統計經過點分中心 的路徑有多少在 中。
暴力算法 中, 是 的條件是 在 的後綴自動機中能夠匹配,且 endpos 集合包含 。注意這裏是在 的前面添加一個點,在後綴自動機上不是走轉移邊,而是走 樹的轉移邊:(下面這張圖截自洛谷題解區 WAPER420 的題解)