HDU 4513 吉哥系列故事——完美队形II

题意:

求一个最长的完美队形,满足左右对称,从左到中间身高需保证不下降

题解:

在manacher模板的基础上增加改动,

 while(newStr[i-p[i]]==newStr[i+p[i]] &&  newStr[i-p[i]]<=newStr[i-p[i]+2] )

多添加一个newStr[i-p[i]]<=newStr[i-p[i]+2]条件,使得所求的回文串满足要求

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define PI acos(-1.0)
#define E 1e-9
#define INF 0x3f3f3f3f
#define LL long long
const int MOD=10007;
const int N=200000+5;
const int dx[]= {
   -1,1,0,0};
const int dy[]= {
   0,0,-1,1};
using namespace std;
int str[N];
int newStr[N*2];
int p[N*2];
int n;
int init(){
   
    newStr[0]=-1;
    newStr[1]=0;
 
    int j=2;
    int len=n;
    for (int i=0;i<len;i++){
   
        newStr[j++]=str[i];
        newStr[j++]=0;
    }
 
    return j;
}
 
int manacher(){
   
    int len=init();
    int res=-1;
 
    int id;
    int mx=0;
    for(int i=1;i<len;i++){
   
        int j=2*id-i;
        if(i<mx)
            p[i]=min(p[j], mx-i);
        else
            p[i]=1;
 
        while(newStr[i-p[i]]==newStr[i+p[i]] &&  newStr[i-p[i]]<=newStr[i-p[i]+2] )
            p[i]++;
 
        if(mx<i+p[i]){
   
            id=i;
            mx=i+p[i];
        }
        res=max(res,p[i]-1);
    }
    return res;
}
 
int main(){
   
    int t;
    scanf("%d",&t);
    while(t--)
    {
   
        scanf("%d",&n);
        for(int i=0;i<n;++i)
            scanf("%d",&str[i]);
        printf("%d\n",manacher());
    }
    return 0;
}

HDU 3613 Best Reward

题意:

一个由26个字母组成的项链,中间切开分成两部分
两部分必须是回文串,否则价值为0,26个字母的价值会给出
问两个部分的价值和是多少

题解:

先求出项链价值的前缀和
跑一遍manacher
我们要将回文串分为两部分,然后分别枚举两部分的中心,判断左右两部分是否为回文串,并记录各自的价值,最后更新最大价值

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
 
const int mn = 500010;
 
char ch[mn];
int val[30], VAL[mn];
 
char temp[2 * mn];
int tp[2 * mn];
void manacher(char ch[])
{
   
	int len = strlen(ch);
 
	temp[0] = '@';
	for (int i = 1; i <= 2 * len; i += 2)
	{
   
		temp[i] = '#';
		temp[i + 1] = ch[i / 2];
	}
	temp[2 * len + 1] = '#';
	temp[2 * len + 2] = '$';
	temp[2 * len + 3] = '\0';
	int tlen = 2 * len + 1;
 
	int mx = 0, id = 0;
	for (int i = 1; i <= tlen; i++)
	{
   
		if (mx >= i)
			tp[i] = min(tp[2 * id - i], mx - i + 1);
		else
			tp[i] = 1;
 
		while (temp[i - tp[i]] == temp[i + tp[i]])
			tp[i]++;
 
		if (mx < i + tp[i] - 1)
		{
   
			mx = i + tp[i] - 1;
			id = i;
		}
	}
}
 
int main()
{
   
	int T;
	scanf("%d", &T);
	while (T--)
	{
   
		for (int i = 0; i < 26; i++)
		{
   
			int t;
			scanf("%d", &t);
			val[i] = t;
		}
		scanf("%s", ch);
		int len = strlen(ch);
 
		VAL[0] = val[ch[0]- 'a'];
		for (int i = 1; i < len; i++)
			VAL[i] = VAL[i - 1] + val[ch[i] - 'a'];
 
		manacher(ch);
 
		int ans = -0x3f3f3f3f;
		int tlen = 2 * len + 1;
		for (int i = 3; i < tlen; i++)
		{
   	/// 枚举切割点
			if (temp[i] == '#')
			{
   
				int l = 0, r = 0;
				if (tp[(i + 1) / 2] == (i + 1) / 2) // 左侧回文
					l = VAL[i / 2 - 1];
				if (tp[(i + tlen) / 2] == (tlen - i) / 2 + 1) // 右侧回文
					r = VAL[len - 1] - VAL[i / 2 - 1];
				ans = max(ans, l + r);
			}
		}
		printf("%d\n", ans);
	}
}

HDU 3068 最长回文

题意:

求回文串的长度

题解:

裸题套模板

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 22000010;
char s[N];
char str[N];
int p[N];
int init() {
   
    int len = strlen(s);
    str[0] = '@', str[1] = '#';
    int j = 2;
    for (int i = 0; i < len; ++i) str[j++] = s[i], str[j++] = '#';
    str[j] = '\0';
    return j;
}
int manacher() {
   
    int ans = -1, len = init(), mx = 0, id = 0;
    for (int i = 1; i < len; ++i) {
   
        if (i < mx) p[i] = min(p[id * 2 - i], mx - i);
        else p[i] = 1;
        while (str[i + p[i]] == str[i - p[i]]) p[i]++;
        if (p[i] + i > mx) mx = p[i] + i, id = i;
        ans = max(ans, p[i] - 1);
    }
    return ans;
}
 
int main() {
   
	while(cin>>s)
	{
   
		 cout << manacher()<<endl;
	}
  // cin >> s;
   
    return 0;
}
//abahabuk 

HDU 5371 Hotaru’s problem

题意:

找一个序列,满足第一部分和第三部分一样,第一部分和第二部分是对称的。
例如2,3,4,4,3,2,2,3,4
求最长长度是多少?

题解:

满足题目要求的序列,就是两个相邻的回文串,共享中间的一部分
我们可以认为,
左边的回文串长度的一半>=共享部分的长度
右边的回文串长度的一半>=共享部分的长度
不可能小于,不然中间共享部分就不成立了
所以我们根据左边的回文串长度的一半,来判断右边回文串是否符合要求,如果如何就记录最大值
如果左边回文串的中心是i
那么右边回文串的中心就是i+p[i]-1

while( len>an && p[i+len]-1-len<0 )

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100000*3;
int s[maxn],str[maxn],p[maxn];
int len1,len2;
void init()
{
   
    str[0]=-2;
    str[1]=0;
    for(int i=0;i<len1;i++){
   
        str[i*2+2]=s[i];
        str[i*2+3]=0;
    }
    len2=len1*2+2;
    str[len2]=-3;
}
void manacher()
{
   
    init();
    int id=0,mx=0;
    for(int i=1;i<len2;i++){
   
        if(mx>i)
            p[i]=min(p[2*id-i],mx-i);
        else
            p[i]=1;
        for(;str[i+p[i]]==str[i-p[i]];p[i]++);
        if(p[i]+i>mx){
   
            mx=p[i]+i;
            id=i;
        }
    }
}
int main()
{
   
    int t;
    scanf("%d",&t);
    int cas=0;
    while(t--){
   
        cas++;
        scanf("%d",&len1);
        for(int i=0;i<len1;i++){
   
            scanf("%d",&s[i]);
        }
        manacher();
        int sum=0;
		for(int i=3;i<len2;i+=2)
		{
   
			if(p[i]-1>sum)
			{
   
				int len=p[i]-1;
				while( len>sum && p[i+len]-1<len)
					len--;
				sum=max(sum,len);
			}
		}
		printf("Case #%d: %d\n",cas,sum/2*3);
    }
}

ABB (2020牛客国庆集训派对day1)

题意:

长度为n的字符串,问最少添加多少字符可以使其构成回文字符串

题解:

最长回文字符串我的第一反应是manacher马拉车算法,那我们直接马拉车找到已有最长回文串,然后总长度减去不就是答案吗?非也 ~ ~ 。注意是让我们构造最长回文字符串,我们会发现,如果我们用马拉车找到的最长回文串的最右端不是字符串最右端,那此情况就相当于作废
比如:
murderforajarofz
我们可以找到最长回文串forajarof,但是最后一位z并不在里面,那你无论怎么构造也用不到forajarof这个回文串,也就是我们要找最长的带最后一个字符的回文字符串
我们直接在manacher的基础上改就可以,原本式子中的ans,我们每查找完一次清零,也就是如果找到的回文串不是带尾的不要,如果带尾保留最大值
详细看代码

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 9*1e5+4;
char s[maxn];
char str[maxn];
int p[maxn];//表示以i为中心的最长回文子串长度,

int init() {
   
    int len = strlen(s);
    str[0] = '@', str[1] = '#';
    int j = 2;
    for (int i = 0; i < len; ++i) str[j++] = s[i], str[j++] = '#';
    str[j] = '\0';
// cout<<j<<endl;
    return j;
}
int manacher() {
   
   
	int len = init(),sum=-1; 
	int mx = 0;//同时记录一个当前延伸最远的回文子串 
	int id = 0;//对称中心 
	int ans = -1;
    for (int i = 1; i < len; ++i) {
   
    	
        if (i < mx) p[i] = min(p[id * 2 - i], mx - i);
        else p[i] = 1;
        while (str[i + p[i]] == str[i - p[i]]) p[i]++;
        //if(i+p[i]==len-1) 
        if (p[i] + i > mx) mx = p[i] + i, id = i;
     // cout<<"id="<<id<<endl;
    	ans = max(ans, p[i] - 1);
		if(mx==len)sum=max(sum,ans);
		
		//cout<<"ans="<<ans<<endl;
    	//cout<<"mx="<<mx<<endl;
    	//cout<<"sum="<<sum<<endl;
 		ans=0;
	}
	return sum;
}
int main() 
{
   
	int t;
	cin>>t;
	for(int i=0;i<t;i++)cin>>s[i];
	if(manacher()==0)
	cout<<t-1<<endl;
	else 
  	cout <<t-manacher()<<endl;
    return 0;
}