• 题解-CmdOI2019 口头禅


    题面

    CmdOI2019 口头禅

    (n)(01)(s_i)(m) 个询问问 (s_{lsim r}) 的最长公共子串长度。

    数据范围:(1le nle 20000)(1le mle 10^5)(sum |s_i|le 4cdot 10^5)


    蒟蒻语

    蒟蒻看到这个题口胡了一个做法,然后轻松拿到了最优解,发现这是道大水题。

    什么猫树或分治我没听说过,反正广义 SAM 上枚举子串乱搞 (Theta(nsqrt nlog n)) 跑得飞快。


    蒟蒻解

    首先把串建成广义 SAM 没有问题,为了方便我写了盗版的 /ch

    离线询问,把 ([l_i,r_i]) 这个询问挂到 (r_i) 上。

    顺序枚举 (r),同时干这些坏事:

    (r_i=r) 的询问按 (l_i) 排序,设有 (qn) 个询问。

    对于 SAM 的节点 (p),维护 (li_p)(ri_p),表示 (s_{1sim r}) 中这个以这个节点为子串的最右连续区间。

    维护方法是枚举 (s_r) 的所有子串(暴力跳每个前缀的 (fa),时间复杂度 (Theta(nsqrt n))),通过 (r-1) 递推。

    然后对于每个该串子串节点 (p)lower_bound 找到第一个 (l_ige li_p),对询问 ([i,qn]) 的答案都与该节点代表最长串长度取 (max)

    这东西根据单调性差分一下即可,然后就做完了。


    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    //Start
    typedef long long ll;
    typedef double db;
    #define mp(a,b) make_pair((a),(b)) 
    #define x first
    #define y second
    #define be(a) (a).begin()
    #define en(a) (a).end()
    #define sz(a) int((a).size())
    #define pb(a) push_back(a)
    #define R(i,a,b) for(int i=(a),I=(b);i<I;i++)
    #define L(i,a,b) for(int i=(b)-1,I=(a)-1;i>I;i--)
    const int iinf=0x3f3f3f3f;
    const ll linf=0x3f3f3f3f3f3f3f3f;
    
    //Data
    const int N=2e4,lN=4e5,qN=1e5;
    int n,qn,ans[qN];
    string s[N];
    vector<pair<int,int>> que[N];
    
    //SuffixAutoMoton
    const int tN=(lN<<1)+1,cN=2;
    int tn,ch[tN][cN],fa[tN],len[tN];
    int newsam(){
    	fill(ch[tn],ch[tn]+cN,-1),fa[tn]=-1;
    	return tn++;
    } 
    int rt=newsam(),t;
    void extend(int c){
    	int p=t,np=t=newsam();
    	len[np]=len[p]+1;
    	for(;~p&&!~ch[p][c];p=fa[p]) ch[p][c]=np;
    	if(!~p) fa[np]=rt;
    	else {
    		int q=ch[p][c];
    		if(len[q]==len[p]+1) fa[np]=q;
    		else {
    			int nq=newsam();
    			copy(ch[q],ch[q]+cN,ch[nq]);
    			len[nq]=len[p]+1,fa[nq]=fa[q],fa[q]=fa[np]=nq;
    			for(;~p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
    		}
    	}
    } 
    int li[tN],ri[tN],vis[tN];
    
    //Main
    int main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0); 
    	cin>>n>>qn;
    	R(i,0,n){
    		cin>>s[i],t=rt;
    		for(char c:s[i]) extend(c-'0');
    	}
    	R(i,0,qn){
    		int l,r; cin>>l>>r,--l,--r;
    		que[r].pb(mp(l,i));
    	}
    	fill(vis,vis+tn,-1);
    	fill(li,li+tn,-2),fill(ri,ri+tn,-2);
    	R(i,0,n){
    		sort(be(que[i]),en(que[i]));
    		vector<int> mx(sz(que[i])+1);
    		int p=rt,now=0;
    		for(char c:s[i]){
    			int q=p=ch[p][c-'0']; now++;
    			for(;~q&&vis[q]<i;q=fa[q]){
    				vis[q]=i;
    				if(ri[q]==i-1) ri[q]=i;
    				else li[q]=ri[q]=i;
    				int id=lower_bound(be(que[i]),en(que[i]),
    					mp(li[q],-1))-be(que[i]);
    				mx[id]=max(mx[id],min(len[q],now));
    			}
    		}
    		R(j,0,sz(que[i])) mx[j+1]=max(mx[j+1],mx[j]);
    		R(j,0,sz(que[i])) ans[que[i][j].y]=mx[j]; 
    	}
    	R(i,0,qn) cout<<ans[i]<<"
    ";
    	return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    gevent
    pymongo的数组操作
    pymongo的聚合操作
    python操作redis的情况总结
    协程
    3
    6
    10
    4
    5
  • 原文地址:https://www.cnblogs.com/George1123/p/13746536.html
Copyright © 2020-2023  润新知