zoukankan      html  css  js  c++  java
  • Gym101237C The Palindrome Extraction Manacher、SAM、倍增

    传送门


    假设字符串(B,D)满足(|B| geq |D|),那么一定会有(B=rev(D)+T),其中(T)是一个回文串。

    考虑枚举回文串(T)的中心(p),找到以(p)为中心的最长回文串(S[l,r])。值得注意的是,回文串越长一定越好,因为如果回文串不是最长的,那么可以向左右拓展一位,(B)串长度会(+1)(D)串长度会(-1),没有变化。所以我们直接Manacher求最长回文串即可。

    现在我们需要求的就是最小的(i)满足(rev(S[i,l-1]))(S[r+1,N])的子串。

    发现对于所有可能的(i)都是(S[1,l-1])的后缀。子串、后缀相关的问题可以考虑后缀自动机。我们对(rev(S))建立SAM,对于SAM上的每一个节点记录最小的(endpos),并将(S)的所有前缀放到SAM上匹配。那么(S[i,l-1])是SAM的parent树上(S[1,l-1])的祖先。在parent树上倍增就可以得到满足条件的最小的(i)

    对于原串和反串各做一遍就可以得到最后的答案。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    //This code is written by Itst
    using namespace std;
    
    #define PII pair < int , int >
    const int MAXN = 2e5 + 7;
    char s[MAXN];
    int ind[MAXN] , len[MAXN] , L , ans;
    PII ind1 , ind2;
    bool flg = 0;
    
    namespace SAM{
    	char S[MAXN];
    	int Lst[MAXN] , trans[MAXN][26] , fa[MAXN] , endpos[MAXN] , jump[MAXN][19];
    	int cnt = 1 , lst = 1;
    	
    	void insert(int l , int x){
    		int p = lst , t = ++cnt; lst = t;
    		Lst[t] = endpos[t] = l;
    		while(p && !trans[p][x]){trans[p][x] = t; p = fa[p];}
    		if(!p){fa[t] = 1; return;}
    		int q = trans[p][x];
    		if(Lst[q] == Lst[p] + 1){fa[t] = q; return;}
    		int k = ++cnt; endpos[k] = 1e9;
    		Lst[k] = Lst[p] + 1;
    		fa[k] = fa[q]; fa[q] = fa[t] = k;
    		memcpy(trans[k] , trans[q] , sizeof(trans[q]));
    		while(trans[p][x] == q){trans[p][x] = k; p = fa[p];}
    	}
    	
    	vector < int > ch[MAXN];
    	
    	void dfs(int x){
    		for(int i = 1 ; jump[x][i - 1] ; ++i)
    			jump[x][i] = jump[jump[x][i - 1]][i - 1];
    		for(auto t : ch[x]){jump[t][0] = x; dfs(t); endpos[x] = min(endpos[x] , endpos[t]);}
    	}
    	
    	void init(){
    		memcpy(S , s , sizeof(S)); reverse(S + 1 , S + L + 1);
    		for(int i = 1 ; i <= L ; ++i) insert(i , S[i] - 'a');
    		for(int i = 2 ; i <= cnt ; ++i) ch[fa[i]].push_back(i);
    		dfs(1);
    	}
    
    	int query(int x , int pos){
    		if(endpos[x] <= pos) return x;
    		for(int i = 18 ; i >= 0 ; --i)
    			if(endpos[jump[x][i]] > pos)
    				x = jump[x][i];
    		return jump[x][0];
    	}
    	
    	void clear(){
    		memset(trans , 0 , sizeof(trans)); memset(Lst , 0 , sizeof(Lst));
    		memset(fa , 0 , sizeof(fa)); memset(endpos , 0 , sizeof(endpos));
    		memset(jump , 0 , sizeof(jump));
    		for(int i = 1 ; i <= cnt ; ++i) ch[i].clear();
    		cnt = lst = 1;
    	}
    }
    
    namespace Manacher{
    	char S[MAXN];
    	int Len[MAXN];
    
    	void work(){
    		for(int i = 1 ; i <= L ; ++i)
    			S[(i << 1) - 1] = s[i];
    		memset(Len , 0 , sizeof(Len));
    		int maxR = 1 , maxI = 1;
    		for(int i = 1 ; i < L << 1 ; ++i){
    			Len[i] = min(maxR - i , Len[2 * maxI - i]);
    			while(i - Len[i] >= 0 && i + Len[i] <= L << 1 && S[i - Len[i]] == S[i + Len[i]])
    				++Len[i];
    			int posL = (i - Len[i] + 1) / 2 + 1 , posR = (i + Len[i] + 1) / 2 - 1;
    			if(posL <= posR){
    				int u = SAM::query(ind[posL - 1] , L - posR) , l = u == ind[posL - 1] ? len[posL - 1] : SAM::Lst[u];
    				if(ans < posR - posL + 1 + 2 * l){
    					ans = posR - posL + 1 + 2 * l;
    					ind1 = PII(posL - l , posR);
    					if(l) ind2 = PII(L - SAM::endpos[u] + 1 , L - SAM::endpos[u] + l);
    					else ind2 = PII(-1 , -1);
    					flg = 1;
    				}
    			}
    			if(maxR <= Len[i] + i){
    				maxR = Len[i] + i;
    				maxI = i;
    			}
    		}
    	}
    }
    
    void work(){
    	flg = 0;
    	SAM::init(); ind[0] = 1;
    	for(int i = 1 ; i <= L ; ++i){
    		int cur = ind[i - 1] , l = len[i - 1];
    		while(cur && !SAM::trans[cur][s[i] - 'a']) l = SAM::Lst[cur = SAM::fa[cur]];
    		!cur ? ind[i] = 1 : (ind[i] = SAM::trans[cur][s[i] - 'a'] , len[i] = l + 1);
    	}
    	Manacher::work();
    }
    
    int main(){
    	scanf("%s" , s + 1);
    	L = strlen(s + 1); work();
    	reverse(s + 1 , s + L + 1); SAM::clear(); work();
    	if(flg){
    		swap(ind2 , ind1); swap(ind1.first , ind1.second); swap(ind2.first , ind2.second);
    		ind1.first = L + 1 - ind1.first; ind1.second = L + 1 - ind1.second;
    		ind2.first = L + 1 - ind2.first; ind2.second = L + 1 - ind2.second;
    	}
    	printf("%d
    %d %d
    %d %d
    " , ans , ind1.first , ind1.second , ind2.first , ind2.second);
    	return 0;
    }
    
  • 相关阅读:
    C# 文件压缩与解压(ZIP格式)
    sqlite 报 no such table 错误
    又一次的轮回
    什么是数据结构
    紧张繁忙的一周
    《编程匠艺》读书笔记之十九
    [转]软件开发者面试百问
    关于学习设计模式的一些废话
    雷人的山寨版搜索引擎
    [转]Struts 2.1发布
  • 原文地址:https://www.cnblogs.com/Itst/p/10527099.html
Copyright © 2011-2022 走看看