马拉车算法(Manacher's Algorithm) //模板

manacher算法可以在O(n)时间内求出一个字符串里每一个位置的最长回文串的长度。

https://www.bilibili.com/video/av4829276

跟着上面的视频学两遍就会了。

 

直接上模板题(manacher模板

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn=3e5+5;
char s[maxn],str[maxn]; //s原串,str马拉车串
int le1,le2,ma[maxn]; //ma[i] i为中心位置的最大长度+1, 若字符串为aaa,i为第二个a的位置,答案为4
void manacher(){
    str[0]='$'; //左边hash
    str[1]='#';
    for(int i=0;i<le1;i++){ //按照马拉车隔开
        str[i*2+2]=s[i];
        str[i*2+3]='#';
    }
    le2=le1*2+2;
    str[le2]='@';//右边hash
    int id=0,mx=0; //mx:能延展到的最右边的位置,  id能延展到最右边位置的字符串的中间位置。
    for(int i=1;i<le2;i++){  //边界为最左边#到最右边#
        if(mx>i){
            ma[i]=min(ma[2*id-i],mx-i);
        }
        else ma[i]=1;  //最大长度+1
        while(str[i+ma[i]]==str[i-ma[i]]) ma[i]++; //暴力找
        if(ma[i]+i>mx){ //跟新当前最大mx位置和中心位置
            mx=ma[i]+i;
            id=i;
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    while(cin>>s){
        le1=strlen(s);
        manacher();
        int ans=0;
        for(int i=0;i<le2;i++)
            ans=max(ans,ma[i]);
        cout<<ans-1<<endl;
    }
    return 0;
}

Codeforces Round #524 (Div. 2) E. Sonya and Matrix Beauty  //hash+马拉车

http://codeforces.com/contest/1080

题意:求满足下列条件的子矩阵数量:重排后,每一行是回文矩阵,每一列也是回文矩阵。

 

大致思路是,将子矩阵每一行hash出一个元素(数组),重写cmp函数,那么n行就是n个元素,就可以对这n个元素用manacher找回文串个数了。

 

1.将每一个子矩阵的每一行hash到一个26大小的数组,表示a-z的字母数量。

2.如果一行有>1个奇数数量的字母,那么包含此行的矩阵就不可能是回文矩阵。

3.要利用到dp思想,矩阵转移时,可以利用到上一个矩阵。

复杂度O(n^3)。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mod 1000000007
const int maxn=255;
/***************************************************************************************************************************************************************************************************/
int n,m,ans;
char mp[maxn][maxn];
int str[maxn*3][27],odd[maxn*3]; //odd记录奇数个数
int ma[maxn*3];
bool cmp(int x,int y){
	if(odd[x]>1||odd[y]>1) return 0;
	for(int i=0;i<26;i++){
		if(str[x][i]!=str[y][i])return 0;
	}
	return 1;
}

void manacher(){
	odd[1]=2;  //抠清楚边界
	int le2=2*n+3;
	odd[le2]=2;
	int id=0,mx=0;
	for(int i=2;i<le2;i++){
        if(odd[i]>1)continue;  //如果中间行不满足,那么对ans无影响,直接continue。
		if(mx>i){
			ma[i]=min(ma[2*id-i],mx-i);
		}
		else ma[i]=1;
		while(cmp(i+ma[i],i-ma[i])) ma[i]++;
		if(ma[i]+i>mx){
			mx=ma[i]+i;
			id=i;
		}
		ans=ans+ma[i]/2;
	}
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=m;j++)
    		cin>>mp[i][j];
    }
    for(int i=1;i<=m;i++){//枚举左边
    	for(int j=1;j<=n;j++){ //勿忘,每次枚举左边,都要初始化
    		for(int xx=0;xx<26;xx++){ 
    			str[j*2+1][xx]=0;
    			odd[j*2+1]=0;
    		}
    	}
    	for(int j=i;j<=m;j++){ //枚举右边,并转移矩阵
    		for(int xx=1;xx<=n;xx++){
    			str[xx*2+1][mp[xx][j]-'a']++;
    			if(str[xx*2+1][mp[xx][j]-'a']&1) odd[xx*2+1]++; 
    			else odd[xx*2+1]--;
    		}
    		manacher();
    	}
    }
    cout<<ans;
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值