我是吐槽,你看不到我~~ (*/ω\*) 清北学堂学习第七天来了个特别漂亮的小姐姐(syq)讲了字符串算法,如 kmp,manacher,hash…还特别安利说manacher可以O(n)复杂度解决回文子串问题233 上课只顾着看小姐姐了。下午考试她说有上午学的一部分内容,说帮我们巩固一下。道理我都懂,可是上来第一题就是manacher算法是要闹那样啊,内流满面T^T。。。我上午只看了看思想啊QAQ。。。一定要把模板保存下来。。。
回文串问题
最长回文子串问题:给定一个字符串,求它的最长回文子串长度(n<=10^7)
*回文串:正序和逆序相等的串。从中心轴出发左右对称,中心轴可以是一个字符,也可以是两个字符间的一个间隙。
暴力吗?
枚举每个子串暴力判断…n^3 !!
那优化一下?
枚举中心轴看最长扩展长度。
那字符间隙呢?
在字符间隙里再插上一个#就好了… n^2!(保证#在原串中没有出现过)
还能优化吗?
发现形如 ababababa 发现枚举第3个b时的回文串在枚举第三个a时已经被匹配过一遍了,考虑能不能像kmp算法一样利用之前的匹配信息?
答案是可以的!
Manacher算法
在O(n)时间内,计算出一个字符串以每个位置为中心轴(一个字符或者字符间隙)最长能扩展出多长的回文串。
预处理:将所有的字符都转为奇数长度。
aba–> #a#b#a#
abba–>#a#b#b#a#
另,设r[i]为以i为中心轴的最长匹配半径(不包括i)
栗子:
# a # b # a #
0 1 0 3 0 1 0
# a # b # b # a #
0 1 0 1 4 1 0 1 0
发现r[i]恰好就是以i为中心的最长回文串长度。
能否快速求出r?
定义两个变量:
pos=一个当前向右延伸的位置最远的回文串的中心轴。
maxn_r=这个中心轴的r。
前提:i>pos
计算r[i]时分三种情况讨论:
① i < maxn_r 时: 取i关于pos的对称点j
由回文串性质,可知
⑴ 若r[j]+i<=maxn_r , r[i]=r[j];
⑵若r[j]+i>maxn_r,
可知 i~maxn_r一段一定是回文串。
从maxn_r+1开始逐位匹配并更新maxn _r和pos。
maxn_r=i+r[i],pos=i;
② i>=maxn_r时 从i往右开始逐位匹配,maxn _r=i+r[i],pos=i;
代码:
void Manacher()
{
l=strlen(b);
for(int i=0;i<l;++i)
{
a[i<<1]='#';
a[i<<1|1]=b[i];
}
l=l*2+1;
a[l-1]='#';
maxn_r=0,pos=0;
for(int i=0;i<l;++i)
{
if(maxn_r>i) r[i]=min(maxn_r-i,r[2*pos-i]);
else r[i]=0;
while(i+r[i]+1<l&&i-r[i]-1>=0&&a[i+r[i]+1]==a[i-r[i]-1]) r[i]++;
if(r[i]+i>maxn_r)
{
maxn_r=r[i]+i;
pos=i;
}
}
}
一道简单题
题目描述
小 A 是个学渣,很快就要到 3000 年 NOIprofessional 初赛了,然而他什么题都不会写。
于是他开始研究往年卷的选项分布(然并卵)。
小 A 有 M 份试题。 由于是 professional 版本的考试,初赛试题题目数量 N 非常大。 小
A 发现,某些年份的答案序列里有很多长度超过 k 的回文子串。 极长回文子串 str 定义
为以答案序列 s 中不存在与 str 同对称轴的更长回文子串。 小 A 想知道,每个年份的答
案序列里有多少长度超过 k 的极长回文子串。
输入格式
第一行两个数字 M,k,表示试卷数和长度下限 k
接下来 M 行,每行一个正整数和一个字符串,表示试题的年份和答案,答案序列不区
分大小写。
输出格式
输出共 M 行,每行两个数,第一个数为年份,第二个数为长度超过 k 的极长回文子串
的数量。按照数量递减,数量相同时年份递增的顺序输出。
样例输入
5 3
2011 bacddcb
2000 cacac
2012 aaacdadd
2008 dcdaa
1999 abcda
样例输出
2000 3
2012 2
2008 1
2011 1
1999 0
格式要求
输入文件 Xuezha.in
输出文件 Xuezha.out
时间限制:1s 内存限制:256MB
数据范围
年份 Y,2000<=Y<3000
对于 30%的数据,1<=M<=10,1<=N<=100,1<=k<=100
对于 100%的数据,1<=M<=100,1<=k<=100000,1<=N<=100000
模板题呦φ(>ω<*)
特别注意!!!题目里是不区分大小写的!!!就是说数据里有大写!!就这一点全场wa了一片,没几个有分的QAQ。 wa的一声就哭了。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int maxn_r,pos,l,k,K,M;
int r[200050];
char a[200050],b[100050];
struct maple{
int year;
long long num;
}Paper[120];
bool cmp(maple a,maple b)
{
if(a.num==b.num) return a.year<b.year;
else return a.num>b.num;
}
void Done()
{
l=strlen(b);
for(int i=0;i<l;++i)
{
a[i<<1]='#';
if(b[i]>='A'&&b[i]<='Z') a[i<<1|1]=b[i]-'A'+'a'; // 单独写会T , mengbi
else a[i<<1|1]=b[i];
}
l=l*2+1;
a[l-1]='#';
maxn_r=0,pos=0;
for(int i=0;i<l;++i)
{
if(maxn_r>i) r[i]=min(maxn_r-i,r[2*pos-i]);
else r[i]=0;
while(i+r[i]+1<l&&i-r[i]-1>=0&&a[i+r[i]+1]==a[i-r[i]-1]) r[i]++;
if(r[i]+i>maxn_r)
{
maxn_r=r[i]+i;
pos=i;
}
}
}
int main()
{
freopen("Xuezha.in","r",stdin);
freopen("Xuezha.out","w",stdout);
scanf("%d%d",&M,&K);
for(int i=1;i<=M;++i)
{
scanf("%d",&Paper[i].year);
Paper[i].num=0;
scanf("%s",b); // 这里用cin,cout也会T
Done();
for(int j=0;j<l;++j)
if(r[j]>=K) ++Paper[i].num;
}
sort(Paper+1,Paper+M+1,cmp);
for(int i=1;i<=M;++i)
{
printf("%d %lld\n",Paper[i].year,Paper[i].num);
}
return 0;
}