HihoCoder第一周与POJ3974:最长回文字串
这个题目是hihoCoder第一周的题目,自己打算从第一周开始做起,不知道能追上多少,更不知道这一篇写完,下一篇会是什么时候。。。
题意很简单。
输入:
3
abababa
aaaabaa
acacdas
输出:
7
5
3
这种经典题目POJ上也有,忘了是第多少道了。
当时我对于Manacher算法不是很理解,其实现在也感觉恍惚。不知道当我写完的时候会不会大彻大悟。
另外,写这篇总结参考了很多大牛博客上的代码,代码可能有雷同,我的锅。但自己的感想是真实的。
第一种方法,直接暴力:
#include <cstdio>
#include <iostream>
#include <string>
using namespace std; void result (string test)
{
int count = test.size();
int start=0,end=count-1,max=0;
int i,j,loop; for(i = start; i < end; i++){
for(j = end; j > start; j--){
for(loop = (j-i)/2 ; loop >= 0;loop--){
if(test[i+ loop] == test[j- loop])
continue;
else
break;
}
if(loop == -1){
if(j-i+1 > max){
max=j-i+1;
} } } }
printf("%d\n",max);
} int main()
{
int count=1;
while(true)
{
string test;
cin>>test;
if(test == "END")
return 1;
else
cout<<"Case "<<count++<<":";
result(test); }
return 0;
}
这种方法不难理解,对于一个字符串
记录其开始节点start,结尾节点end。以start为轴,不断去找使其loop为-1,即是回文的end。依次试,start++。记录最大值即可。
这种算法,因为对字符串循环遍历了两遍,所以时间复杂度O(n平方)。
第二种方法,动态规划:
#include <cstdio>
#include <iostream>
#include <string>
using namespace std; void result (string test)
{
bool testarr[1000][1000] = {false};
int count = test.size(),max = 0; for(int i=0;i<count;i++){ testarr[i][i]=true; if(i+1<count&&test[i]==test[i+1]){ max = 2;
testarr[i][i+1] = true; }
}
for(int len=3;len<=count;len++) for(int i=0;i<=count-len;i++){ int j=i+len-1;
if(testarr[i+1][j-1]&&test[i]==test[j]){ max=len;
testarr[i][j]=true;
} }
cout<<max<<endl;
} int main()
{
int count=1;
while(true)
{
string test;
cin>>test;
if(test == "END")
return 1;
else
cout<<"Case "<<count++<<":";
result(test); }
return 0;
}
一直以来其实都不是很理解动态规划的具体含义,只知道就那么回事。看动态规划的代码总是一看就懂,但具体使用,除非告诉我这道题是动态规划的题,否则就想不到。举一反三的能力一次也没拥有过。。。以后要加强。
这里的testarr[i][j]如果是true的话,说明这个字符串从i到j是回文,即其长度j-i+1。这个题目动态规划的思想在于,如果我已知testarr[i-1][j-1]是true,那么再判断test[i]与test[j]是否相等,如果相等,即把testarr[i][j]标记为true。
有意思的是,这里的外层循环不是遍历字符串,而是查找对应长度的回文,从长度为3开始,看字符串中是否含有长度为3的回文,内层循环从字符串的位置0开始查找。一次找看看最终能不能找到长度为size的回文,找到其中最大值,记录下来。
我把动态规划思想理解为一个由小变大,滚雪球的过程。在过程中产生一些结果,后面的结果又以前面的结果为基础,得到最终答案。
代码有的时候会有这种感受,写完之后再理思路感觉都没什么亮点,但在写的过程中,遇到的bug也真是煎熬。
第三种方法,Manacher方法:
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std; char str[1000005];
int p[1000005<<1];
char a[1000005<<1];
int min(int a,int b){
return a>b?b:a;
}
void result(){
int maxLine=0,ID=1,maxResult=0;
int n=0,i,len,lentmp;
lentmp=strlen(str);
len=lentmp<<1; for(i=0;i<(1000005<<1);i++){
p[i]=0;
a[i]='#';
}
for(i=0;i<lentmp;i++){
a[((i+1)<<1)-1]=str[i]; } for(i=1;i<len;i++){ if(maxLine >i){
p[i]=min(p[2*ID-i],maxLine-i);
}
else{
p[i]=1;
} while(a[i+p[i]]==a[i-p[i]]){
p[i]++; }
if(p[i]+i>maxLine){
maxLine=p[i]+i;
ID=i;
}
if(p[i]>maxResult){
maxResult=p[i];
}
} cout<<maxResult-1<<endl;
}
int main()
{
int count=1;
while(true)
{
cin>>str;
if(strcmp(str,"END")==0)
return 1;
else
cout<<"Case "<<count++<<": ";
result();
}
return 0;
}
刚刚介绍的方法都有弊端,前两种方法的弊端在于我遍历一遍字符串只对一个节点记录信息,就是所有我记录的信息都只对一个节点有用,这就导致我不得不对每一个节点都再遍历一遍,导致时间复杂度n的平方。第三种方法的弊端在于我已经知道字符串最大的回文子串撑破天可能也就是其size,那我就从2开始试呗。
而实际上,我可以试试在遍历一次字符串时得到信息的最大值。
这里变量的含义:
P[i]代表以i为中心时能够到达最远的字符。
maxLine实际上就代表已经扫描到的最右边的字符,即maxLine=P[ID]+ID。
ID代表当前使得扫描到最右边字符的那个位置上的字符。
maxResult 就是记录最终的结果值,即找到最大的那个P[i]。
所以,到这里,整个算法最难理解的实际就是这么一段代码:
if(maxLine>i)
{
p[i]=min(p[2*ID-i],maxLine-i);
}
Else
{
p[i]=1;
}
注意,这里并非直接给P[i]盖棺定论,而只是给P[i]赋一个初始值,后面还要有判断,P[i]是否++。这个赋初始值其实也是整个算法的亮点。总结一下就是csdn上的一篇博客的评论挺经典的:
大家可以想象一下,如果P[2*ID-i]这个值很大很大。由于ID的对称性,阻止P[i]初始值的更大的,恰恰就是maxLine-i,因为更远的还没有比对,不知道结果。
而如果maxLine-i这个值很大很大,就是多远的都已经比对完了。那么也因为ID的对称性,P[i]初始值也即P[2*ID-i]。
自己总结的Manacher算法就这么多,我还很菜,还在努力进步,如果有不对的地方,小弟还望各路大牛指点指正。
版权声明:本文为博主原创文章,未经博主允许不得转载。
HihoCoder第一周与POJ3974:最长回文字串的更多相关文章
- hihocoder 第一周 最长回文字串
题目1 : 最长回文子串 时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程 ...
- POJ 3974 最长回文字串(manacher算法)
题意:给出一个字符串,求出最长回文字串. 思路:一开始我直接上了后缀数组DC3的解法,然后MLE了.看了DISCUSS发现还有一种计算回文字串更加优越的算法,就是manacher算法.就去学习了一下, ...
- 最长回文字串——manacher算法
时间复杂度:O(n) 参考:https://segmentfault.com/a/1190000003914228 1.问题定义 最长回文子串问题:给定一个字符串,求它的最长回文子串长度. 如果一个字 ...
- 求字符串的最长回文字串 O(n)
昨天参加了某公司的校园招聘的笔试题,做得惨不忍睹,其中就有这么一道算法设计题:求一个字符串的最长回文字串.我在ACM校队选拔赛上遇到过这道题,当时用的后缀数组AC的,但是模板忘了没写出代码来. 回头我 ...
- Hdu 3068 最长回文字串Manacher算法
题目链接 最长回文 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- 【POJ3974】最长回文字串
在这里采用的是哈希+二分的方法. 根据回文串的性质可知,可以将回文分成奇回文和偶回文分别进行处理. 对于奇回文来说,每次枚举的端点一定是重合的,因此只需计算出端点左右公共的长度是多少即可,因此二分的是 ...
- 最长回文字串 (The longest palindrome substring)
这两天去学了一下,觉得下面那篇文章写的很好,有例子,比较容易懂,所以转一下. 以下内容来自:hihoCoder: 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互 ...
- 【LeetCode每天一题】Longest Palindromic Substring(最长回文字串)
Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...
- Leetcode5.Longest Palindromic Substring最长回文字串
给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为1000. 示例 1: 输入: "babad" 输出: "bab" 注意: &quo ...
随机推荐
- vue 之 axios Vue路由与element-UI
一. 在组件中使用axios获取数据 1. 安装和配置axios 默认情况下,我们的项目中并没有对axios包的支持,所以我们需要下载安装. 在项目根目录中使用 npm安装包 npm install ...
- Django 学习之Django Rest Framework(DRF)
一. WEB应用模式 在开发Web应用中,有两种应用模式 1. 前后端不分离 把html模板文件和django的模板语法结合渲染完成以后才从服务器返回给客户. 2. 前后端分离 二. API接口 AP ...
- 二分查找及几种变体的Python实现
1. 在不重复的有序数组中,查找等于给定值的元素 循环法 def search(lst, target): n = len(lst) if n == 0: return -1 low = 0 high ...
- Python 文件和目录操作学习
文件与文件路径 文件有两个关键属性:文件名和路径. 路径指明了文件在计算机上的位置. 文件名中,最后一个句点之后的部分称为文件的"扩展名",它指出了文件的类型 目录也叫文件夹,文件 ...
- async处理异步操作
async函数用async作为关键字,try和 catch来处理异常, await接受一个promise函数返回 async list () { try { await api.findjuBarDa ...
- asp.net core配置下载文件
asp.net core的wwwroot文件夹下默认时保存静态文件的地方,外面可以直接访问,但是如果是一些无法识别的后缀文件,如(.apk),会报错404 如果想要实现下载这些文件,在配置静态文件中间 ...
- 在win10中启动redis
1.输入命令"redis-server.exe redis.windows.conf ",按回车键, 2.输入“redis-cli.exe -h 127.0.0.1 -p 6379 ...
- 自学java难不难 搞定这十三步就够了
自学java难不难,对不同的人来说可能不一样,有着编程基础的来说,相对容易.如果是完全小白,就会稍微困难点,下面总结了十三步来自学java的步骤. 第1步:自学JAVA的开发环境配置.开发首个Java ...
- 按钮UIButton的使用
一.使用概要 当添加一个按钮到你的界面,执行以下步骤: 1.在创建时设置按钮的类型. 2.提供一个标题字符串或图像,为您的内容适当调整按钮的大小. 3.连接一个或多个操作按钮的方法. 4.设置自动布局 ...
- Python输出三位数以内的水仙花数
num = 100 while num <= 999: a = num % 10 #取个位数 b = num // 10 % 10 #取十位数 c = num // 100 #取百位数 if n ...