• CF30E Tricky and Clever Password


    XI.CF30E Tricky and Clever Password

    一开始看错题,硬生生把难度上升了很多……

    所以以下的解法是按照我看错的题意进行的,即 \(S=T_1+S_1+T_2+S_2+T_3+S_3+T_4\),其中 \(S_2\) 是奇回文串,\(S_1\)\(S_3\) 相反,除 \(S_2\) 外一切都可为空。

    我们发现,最优情形下,\(S_2\) 一定是极长回文串,因为这样严格不劣于非极长回文串的情形。

    于是我们考虑枚举所有极长回文串(共 \(n\) 个),问题转换为求原串的前缀和后缀的最长公共相反串。考虑对后缀反转,就变成了两个串(即原串以及原串的反串)各自前缀的最长公共子串。

    考虑把两个串放一块跑广义SAM。在每个节点,记录它所对应的 \(\text{endpos}\) 集合中,来自原串的最左端的串和来自反串的最右端的串。考虑对于一个 \(S_2\),只有某些 \(\text{endpos}\) 集合是合法的,当且仅当其上述最左串和最右串全都与 \(S_2\) 无交。假如我们把它看作二维的坐标的话,我们发现问题转换为二维数点问题。直接上BIT维护即可。

    但是这是看错题的解法。正确的题意中,要求 \(T_4=\varnothing\)。这没有关系,只需保留所有最右串恰好是后缀的集合即可。

    时间复杂度 \(O(n\log n)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    struct Suffix_Automaton{
    	int ch[26],mn,mx,len,fa;
    	Suffix_Automaton(){mn=0x3f3f3f3f,mx=-1;}
    }t[400100];
    int cnt=1;
    int Add(int x,int c){
    	if(t[x].ch[c]){
    		int y=t[x].ch[c];
    		if(t[y].len==t[x].len+1)return y;//(x,c) has been added into the tree already.
    		int yy=++cnt;t[yy]=t[y];
    		t[yy].len=t[x].len+1,t[y].fa=yy;
    		for(;x&&t[x].ch[c]==y;x=t[x].fa)t[x].ch[c]=yy;
    		return yy;
    	}
    	int xx=++cnt;t[xx].len=t[x].len+1;
    	for(;x&&!t[x].ch[c];x=t[x].fa)t[x].ch[c]=xx;
    	if(!x){t[xx].fa=1;return xx;}
    	int y=t[x].ch[c];
    	if(t[y].len==t[x].len+1){t[xx].fa=y;return xx;}
    	int yy=++cnt;t[yy]=t[y];
    	t[yy].len=t[x].len+1;
    	t[y].fa=t[xx].fa=yy;
    	for(;x&&t[x].ch[c]==y;x=t[x].fa)t[x].ch[c]=yy;
    	return xx;
    }
    vector<int>v[400100];
    char s[100100];
    int S;
    void chmn(int &x,int y){if(y<x)x=y;}
    void chmx(int &x,int y){if(y>x)x=y;}
    void dfs(int x){for(auto y:v[x])dfs(y),chmn(t[x].mn,t[y].mn),chmx(t[x].mx,t[y].mx);}
    struct dat{
    	int x,y,z;
    	dat(){x=y=z=0;}
    	dat(int X,int Y,int Z){x=X,y=Y,z=Z;}
    	friend bool operator<(const dat&p,const dat&q){return p.z<q.z;}
    }bit[100100];
    bool cmp(dat&p,dat&q){return p.x<q.x;}
    void ADD(int x,dat d){
    	x++;
    	while(x)bit[x]=max(bit[x],d),x-=x&-x;
    }
    dat SUM(int x){
    	x++;
    	dat ret;
    	while(x<=S)ret=max(ret,bit[x]),x+=x&-x;
    	return ret;
    }
    vector<dat>p,q;
    int rad[100100];
    void Manacher(){
    	int Rpos=-1,Centre=-1;
    	for(int i=0;i<S;i++){
    		if(i<Rpos)rad[i]=min(Rpos-i,rad[2*Centre-i]);
    		while(i-rad[i]>=0&&i+rad[i]<S&&s[i-rad[i]]==s[i+rad[i]])rad[i]++;
    		if(i+rad[i]>Rpos)Rpos=i+rad[i],Centre=i;
    		q.push_back(dat(i-rad[i],i+rad[i],rad[i]*2-1));
    	}
    }
    vector<int>rs;
    int mx;
    int main(){
    	scanf("%s",s),S=strlen(s);
    	for(int i=0,las=1;i<S;i++)chmn(t[las=Add(las,s[i]-'a')].mn,i);
    	for(int i=S-1,las=1;i>=0;i--)chmx(t[las=Add(las,s[i]-'a')].mx,i);
    	for(int i=2;i<=cnt;i++)v[t[i].fa].push_back(i);
    	dfs(1);
    	for(int i=1;i<=cnt;i++)if(t[i].mn<t[i].mx&&(t[i].mx+t[i].len==S))p.push_back(dat(t[i].mn,t[i].mx,t[i].len));
    	Manacher();
    	sort(p.begin(),p.end(),cmp),sort(q.begin(),q.end(),cmp);
    	for(int i=0,j=0;i<q.size();i++){
    		while(j<p.size()&&p[j].x<=q[i].x)ADD(p[j].y,p[j]),j++;
    		if(mx<q[i].z)mx=q[i].z,rs={q[i].x+1,q[i].y-1};
    		dat tmp=SUM(q[i].y);
    		if(mx<q[i].z+2*tmp.z)mx=q[i].z+2*tmp.z,rs={tmp.x-tmp.z+1,tmp.x,q[i].x+1,q[i].y-1,tmp.y,tmp.y+tmp.z-1};
    	}
    	printf("%d\n",rs.size()/2);
    	for(int i=0;i<rs.size();i+=2)printf("%d %d\n",rs[i]+1,rs[i+1]-rs[i]+1);
    	return 0;
    } 
    

  • 相关阅读:
    php下的jsonp使用实例
    jquery ajax jsonp跨域调用实例代码
    js/ajax跨越访问-jsonp的原理和实例(javascript和jquery实现代码)
    jsonp实现跨域访问
    jsonp调用实例
    Jsonp和java操作例子
    JSONP实例
    跨平台移动开发工具:PhoneGap与Titanium全方位比拼
    混合开发模式下主流移动开发平台分析
    企业移动信息化应用开发模式选型指南
  • 原文地址:https://www.cnblogs.com/Troverld/p/14605713.html
Copyright © 2020-2023  润新知