zoukankan      html  css  js  c++  java
  • CF914F Substrings in a String

    题面

    英文题面

    题意:给一个串(S),有(q)次操作:

    1 i c表示将(i)位置的字符修改为(c);2 l r t表示求(s_{l},s_{l+1},cdots s_r)(t)串的出现次数。

    (|S|,q,sum |t| leq 10^5)

    题解:不难想到暴力kmp匹配,单次查询的时间复杂度是(O(|S|+|t|))。发现(|t|)较小时这样做很不划算,因此我们可以考虑根号分治。

    (|t|)较小时,如果我们对(S)分块,那么(t)只会有两种情况:完全在块内,或者在两个块之间。

    对于整块,我们对每个块内的字符串都建立SAM,有修改就打个标记,查询时拍扁重建。由于修改次数最多(n)次,所以重构的时间复杂度是(O(n sqrt n))的。

    对于散块,直接kmp即可。

    对于(t)在两个块之间的情况,我们只需要考虑左端点在上一个块,右端点在当前块的位置,因此直接kmp的复杂度也是(O(|t|))的。

    时间复杂度:(O(n sqrt n))

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    typedef long long ll;
    #define I inline void
    #define IN inline int
    #define C(x,y) memset(x,y,sizeof(x))
    #define STS system("pause")
    template<class D>I read(D &res){
    	res=0;register D g=1;register char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')g=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		res=(res<<3)+(res<<1)+(ch^48);
    		ch=getchar();
    	}
    	res*=g;
    }
    const int N=316;
    char c[101000],t[101000],cc;
    int n,m,sum,T,ln,ans,a[101000],b[101000];
    int sit,X,Y,W;
    struct SAM{
    	int p,q,las,tot,cur,cle,len[660],link[660],ch[660][26],cnt[660],buc[660],sa[660],lft,rit,v;
    	I clear(int x){
    		len[x]=link[x]=cnt[x]=0;
    		F(i,0,25)ch[x][i]=0;
    	}
    	I insert(int x){
    		cur=++tot;clear(tot);
    		len[cur]=len[las]+1;p=las;las=cur;cnt[cur]=1;
    		while(p&&!ch[p][x])ch[p][x]=cur,p=link[p];
    		if(!p)return link[cur]=1,void();
    		q=ch[p][x];if(len[p]+1==len[q])return link[cur]=q,void();
    		cle=++tot;clear(cle);len[cle]=len[p]+1;link[cle]=link[q];F(i,0,25)ch[cle][i]=ch[q][i];
    		while(p&&ch[p][x]==q)ch[p][x]=cle,p=link[p];
    		link[q]=link[cur]=cle;
    	}
    	I build(){
    		v=1;tot=las=1;clear(1);
    		F(i,lft,rit)insert(a[i]);
    		F(i,1,tot)buc[i]=0;
    		F(i,1,tot)buc[len[i]]++;
    		F(i,1,tot)buc[i]+=buc[i-1];
    		FOR(i,tot,1)sa[buc[len[i]]--]=i;
    		FOR(i,tot,2)cnt[link[sa[i]]]+=cnt[sa[i]];
    	}
    	I solve(){
    		if(!v)build();
    		re p=1,val;
    		F(i,1,ln){
    			val=t[i]-'a';
    			if(!ch[p][val])return;
    			p=ch[p][val];
    		}
    		ans+=cnt[p];
    	}
    }s[330];
    int w[101000],nt[101000],k;
    I init(){
    	F(i,1,ln)w[i]=t[i]-'a';w[ln+1]=-1;
    	nt[1]=0;k=0;
    	F(i,2,ln){
    		while(k&&w[k+1]!=w[i])k=nt[k];
    		if(w[k+1]==w[i])k++;
    		nt[i]=k;
    	}
    //	F(i,1,ln)cout<<nt[i]<<" ";cout<<endl;
    }
    I kmp(int l,int r){
    	k=0;if(r-l+1<ln)return;
    //	cout<<l<<" "<<r<<":"<<endl;
    	F(i,l,r){
    		while(k&&w[k+1]!=a[i])k=nt[k];
    		if(w[k+1]==a[i])k++;
    		if(k==ln)ans++,k=nt[k];
    	}
    }
    int main(){
    	ios::sync_with_stdio(false);cin.tie(0);
    	cin>>c+1;n=strlen(c+1);F(i,1,n)a[i]=c[i]-'a';
    	F(i,1,n)b[i]=((i-1)/N)+1;sum=b[n];
    	F(i,1,sum)s[i].lft=(i-1)*N+1,s[i].rit=min(i*N,n),s[i].v=0;
    	cin>>m;
    	while(m--){
    		cin>>sit>>X;
    		if(sit==1){
    			cin>>cc;W=cc-'a';
    			a[X]=W;s[b[X]].v=0;
    		}		
    		else{
    			T++;
    			cin>>Y>>t+1;ln=strlen(t+1);ans=0;
    			init();//if(T==104){F(i,1,ln)cout<<w[i]<<" ";cout<<endl;}
    			if(ln>N)kmp(X,Y);
    			else{
    				F(i,b[X]+1,b[Y]-1)s[i].solve();
    				if(b[X]==b[Y])kmp(X,Y);
    				else{
    					kmp(X,s[b[X]].rit);kmp(s[b[Y]].lft,Y);
    					if(ln>1){
    						F(i,b[X],b[Y]-1)kmp(max(X,s[i].rit-ln+2),min(Y,s[i+1].lft+ln-2));
    					}
    				}
    			}
    			cout<<ans<<endl;
    		}
    	}
    	return 0;
    }
    
    
    
  • 相关阅读:
    正则表达式
    文件上传例子
    如何做好数字化体验管理,了解一下?
    云原生背景下故障演练体系建设的思考与实践—云原生混沌工程系列之指南篇
    OpenKruise v1.0:云原生应用自动化达到新的高峰
    Spring Boot Serverless 实战系列“部署篇” | Mall 应用
    阿里云实时数仓Hologres年度发布,解读数仓新趋势
    基于 ASK + EB 构建容器事件驱动服务
    各位 PHPer,Serverless 正当时
    如何在零停机的情况下迁移 Kubernetes 集群
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/13270364.html
Copyright © 2011-2022 走看看