马拉车算法

判断是否为回文串

回文字符串是一种正反颠倒之后还与原来相同的字符串,所以当判定一个字符串是否是回文字符串时,我们可以这样判断:
(1)当字符串长度为奇数时,找到处于中间位置的下标,比较与它相同远的两个字符是否相同即可
(2)长度为偶数时,找到最中间的两个,然后与奇数相同的处理情况即可。

#include<iostream>
#include<string>
using namespace std;

string s;
int solve1(int x)
{
    int l=x-1;  
    int r=x+1;
    while(l>=0&&r<(int)s.size())
    {
        if(s[l]==s[r])
        {
            l--;
            r++;
        }
        else 
       return 0;
    }
      return 1;
}

int solve2(int x)
{   
    int l=x-1;
    int r=x;
    while(l>=0&&r<(int)s.size())
    {
        if(s[l]==s[r])
        {
          l--;
          r++;
        }
        else 
        {
            return 0;
        }
    }
    return 1;
}

int main()
{   
    cin>>s;
    int len=s.size();
    int f;
    if(len%2)   //长度为奇数时
       f=solve1(len/2);
    else        //长度为偶数时
       f=solve2(len/2);
    if(f)
    cout<<"yes"<<endl;
    else 
    cout<<"no"<<endl;
    return 0;
}
最长回文子串

输入一个字符串Str,输出Str里最长回文子串的长度。输入一个字符串Str,输出Str里最长回文子串的长度。
回文串:指aba、abba、cccbccc、aaaa这种左右对称的字符串。
串的子串:一个串的子串指此(字符)串中连续的一部分字符构成的子(字符)串
例如 abc 这个串的子串:空串、a、b、c、ab、bc、abc.
输入Str(Str的长度 <= 1000)
最长回文子串

因为数据范围只有1000,所有可以采用暴力的做法,假设字符串中的每一个字符都处于中间的位置,在分为处于奇数和偶数长度的回文字符串中两种情况,暴力枚举即可

#include<iostream>
#include<string>
using namespace std;
const int N=1e5+10;
string s;
int solve1(int x)  //奇数
{
    int len=1;
    int l=x-1;
    int r=x+1;
    while(l>=0&&r<s.size())
    {
        if(s[l]==s[r])
        {
            l--;
            r++;
            len+=2;
        }
        else 
        break;
    }
    return len;
}
int solve2(int x)  //偶数
{   int len=0;
    int l=x;
    int r=x+1;
    while(l>=0&&r<s.size())
    {
        if(s[l]==s[r])
        {
          l--;
          r++;
          len+=2;
        }
        else 
        break;
    }
    return len;
}
int main()
{   
    cin>>s;
    int ans=0;
    for(int i=0;i<s.size();i++)
    { 
        ans=max(ans,solve1(i));  
        ans=max(ans,solve2(i));
    }    
    cout<<ans<<endl;  //最长回文字符串的长度
    return 0;
}

但当数据范围过大时,这种做法会超时。
例如(Str的长度 <= 100000)
请添加图片描述

这时,就可以使用一种叫manacher的算法,他能以O(n)的复杂度找出最长回文子串。
首先对于奇回文串和偶回文串,可以这样处理:
将abacbb变为#a#b#a#c#b#b#
这样偶回文子串就变成了以#为中心的奇回文子串,原本的子串长度就变成了现在子串长度半径-1。
然后还有一个知识,回文串中如果左半边有一个回文子串,那在右半边相对的位置的子串也是一个回文子串。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <set>
#include <ctime>
#include <utility>
#include <unordered_map>
#define MM(x, y) memset(x, y, sizeof(x))
#define lowbit(x) ((x) & (-x))
#define ll long long
#define INF 0x3f3f3f3f
#define bug puts("bugbugbug");

//priority_queue<int,vector<int>,greater<int> >c;
//priority_queue<int>c;
using namespace std;
const int N=2e5+10;
char a[N],s[N];
int len[N];
int n;
int ans=0;
void init()  //将字符串转换为#a#a#的形式
{
	s[0]=s[1]='#';  
	for(int i=0;i<n;i++)
	{
	  s[i*2+2]=a[i];
	  s[i*2+3]='#';
	}
	n=n*2+2;
	s[n]='!'; //末尾加一个不常用的字符,防止越界
}
void manacher()
{
  int mr=0,mid;      //mr 记录的时已知的回文子串的右边界能到达的最远位置,mid为此时回文子串的中心位置
  for(int i=1;i<n;i++)  //遍历每一个以s[i]为中心的子串长度
  {
	  if(i<mr)              //如果i<mr,那么以i为中心的子串长度有过重复计算,找到关于mid对称的左边位置的回文子串长度,在与mr的距离取最小值即可        
	  len[i]=min(len[2*mid-i],mr-i);  
	  else
	  len[i]=1;         
	  while(s[i-len[i]]==s[i+len[i]])
	  len[i]++;
	  if(i+len[i]>mr)      //更新mr 和mid
	  {
		  mr=i+len[i];
		  mid=i;
	  }
	  ans=max(ans,len[i]);
  }
}
int main()
{
    ans=0;
	MM(len,0);
	scanf("%s",a);
	n=strlen(a);
	init();
	manacher();
	cout<<ans-1<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值