CF 1326 D. Prefix-Suffix Palindrome
D. Prefix-Suffix Palindrome
题意
给一个字符串 s,求一个字符串 t,t 由 s 的某个前缀以及某个后缀拼接而成,且 t 是回文串,长度不能超过 s。输出最长的 t
分析
建议先参考一下官方题解:http://codeforces.com/blog/entry/74961
先考虑 s 的最长border,即找最大的 l,使得 \(s[1..l] = s[n-l+1...n].reverse()\) , 可以想到 t 一定能够完全包含这两部分。
假如不包含,可以把它继续延长让它包含,比如abcxyzcba
,最终答案一定包含abc
以及cba
两部分。
在求出最长的border之后,考虑剩余的中间部分,要找的就是最长回文前后缀,然后比较一下长度即可。
最长回文前缀如何求?可以回文自动机,可以马拉车,但也可以\(KMP\)。
前两种基本的是裸的做法,如果用\(KMP\)处理,则需要将串反过来接到后面(中间用特殊字符隔开),然后跑一边取nxt[2*len+1]
即可。
最长回文后缀就是将原串倒过来处理
KMP解法
#include <bits/stdc++.h>
using namespace std;
const int N = 2000010;
char s[N], t[N];
int nxt[N], n;
int getnxt(char *s, int n){
nxt[1] = 0;
for(int i = 2, j = 0; i <= n; i++){
while(j > 0 && s[i] != s[j+1]) j = nxt[j];
if(s[i] == s[j+1]) j++;
nxt[i] = j;
}
return nxt[n];
}
int get(char *t, int len){
t[len+1] = '#';
for(int i = 1;i <= len; i++){
t[i + len + 1] = t[len - i + 1];
}
return getnxt(t, 2*len + 1);
}
void print(int l, int r){
for(int i=l;i<=r;i++)printf("%c", s[i]);
}
int main(){
int T;scanf("%d", &T);
while (T--)
{
scanf("%s", s+1);
n = strlen(s + 1);
int l = 0, r = n + 1;
while(l + 1 < r - 1 && s[l + 1] == s[r - 1]) l++, r--;
int len = 0;
for(int i = l + 1; i <= r - 1; i++){
t[++len] = s[i];
}
// 得到[l+1, r-1]区间的最长回文前缀
int pre_len = get(t, len);
reverse(t + 1, t + 1 + len);
// 得到[l+1, r-1]区间的最长回文后缀
int suf_len = get(t, len);
print(1, l);
if(pre_len > suf_len) print(l + 1, l+pre_len);
else print(r - suf_len, r - 1);
print(r, n);
puts("");
}
return 0;
}
Manacher解法:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n;
char s[N], t[N];
char ma[N*2];
int mp[N*2];
int L[2*N], R[2*N], pre_len, suf_len;
//L[i] 表示以 i 为右端点的最长回文串长度,R[i]表示以 i 为左端点的最长回文长度
void Manacher(char s[], int len){
int l = 0;
ma[l++] = '$';
ma[l++] = '#';
for(int i=1; i <= len;i++){ // 填充字符串,注意s[i] 填到了 ma[i*2] 的位置
ma[l++] = s[i];
ma[l++] = '#';
}
ma[l] = 0;
int mx = 0, id = 0;
for(int i=0;i<l;i++){
mp[i] = mx > i ? min(mp[2 * id - i], mx - i) : 1;
while(ma[i + mp[i]] == ma[i - mp[i]]) mp[i] ++;
if(i + mp[i] > mx){
mx = i + mp[i];
id = i;
}
if(mp[i] > 1){ // i+mp[i]-2以及 i-mp[i]+2 可以保证一定是原串字符对应的位置,而非特殊字符
L[i + mp[i] - 2] = max(L[i + mp[i] - 2], mp[i] - 1);
R[i - mp[i] + 2] = max(R[i - mp[i] + 2], mp[i] - 1);
}
}
for(int i = 2; i < l; i += 2){ // i=2位置对应第一个原串字符,每次+2表示接着下一个原串字符
R[i] = max(R[i], R[i - 2] - 2);
}
for(int i = l - 3; i >= 2; i -= 2){
L[i] = max(L[i], L[i + 2] - 2);
}
pre_len = R[2]; // 取最长回文前缀长度
suf_len = L[2*len]; // 取最长回文后缀长度
for(int i=0;i<l;i++) L[i] = R[i] = 0;
}
void print(int l, int r){
for(int i=l;i<=r;i++)printf("%c", s[i]);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%s", s+1);
n = strlen(s + 1);
int l = 0, r = n + 1;
while(l + 1 < r - 1 && s[l + 1] == s[r - 1]) l++, r--;
int len = 0;
for(int i = l + 1; i <= r - 1; i++){
t[++len] = s[i];
}
t[len+1] = 0;
Manacher(t, len);
print(1, l);
if(pre_len > suf_len) print(l + 1, l+pre_len);
else print(r - suf_len, r - 1);
print(r, n);
puts("");
}
}
回文自动机解法
const int N = 1000000 + 5;
int n;
char s[N], t[N], p[N];
struct PAT{
int ch[N][26],fail[N],len[N],pos[N],tot,last;
void init(){
for(int i=0;i<=tot;i++){
fail[i] = len[i] = 0;
for(int j=0;j<26;j++)ch[i][j] = 0;
pos[i] = 0;
}
s[0] = -1;fail[0] = 1;last = 0;
len[0] = 0;len[1] = -1;tot = 1;
}
inline int newnode(int x){
len[++tot] = x;return tot;
}
inline int getfail(char *s, int x,int n){
while(s[n-len[x]-1] != s[n])x = fail[x];
return x;
}
void create(char *s){
s[0] = -1;
for(int i=1;s[i];++i){
int t = s[i] - 'a';
int p = getfail(s, last,i);
if(!ch[p][t]){
int q = newnode(len[p]+2);
fail[q] = ch[getfail(s, fail[p],i)][t];
ch[p][t] = q;
}
pos[i] = ch[p][t];
last = ch[p][t];
}
}
}pre, suf;
void print(int l, int r){
for(int i=l;i<=r;i++)printf("%c", s[i]);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
pre.init();
suf.init();
scanf("%s", s+1);
n = strlen(s+1);
int l = 0, r = n+1;
while(s[l+1] == s[r-1] && l+1 < r-1) l++, r--;
if(l == r - 1){
print(1, n);
puts("");
continue;
}
int len = 0;
for(int i=l+1;i<=r-1;i++){
t[++len] = s[i];
}
t[len+1] = 0;
for(int i=1;i<=len;i++){
p[i] = t[len - i + 1];
}
p[len+1] = 0;
pre.create(t);
suf.create(p);
if(pre.len[pre.pos[len]] > suf.len[suf.pos[len]]){
print(1, l);
print(r - pre.len[pre.pos[len]], n);
}
else{
print(1, l + suf.len[suf.pos[len]]);
print(r, n);
}
puts("");
}
return 0;
}
CF 1326 D. Prefix-Suffix Palindrome的更多相关文章
- mybatis之<trim prefix="" suffix="" suffixOverrides="" prefixOverrides=""></trim>的含义
转自:http://blog.csdn.net/qq_33054511/article/details/70490046 <trim prefix="" suffix=& ...
- mybatis之<trim prefix="" suffix="" suffixOverrides="" prefixOverrides=""></trim>
1.<trim prefix="" suffix="" suffixOverrides="" prefixOverrides=&quo ...
- CSAcademy Prefix Suffix Counting 题解
CSAcademy Prefix Suffix Counting 题解 目录 CSAcademy Prefix Suffix Counting 题解 题意 思路 做法 程序 题意 给你两个数字\(N\ ...
- [LeetCode] Prefix and Suffix Search 前后缀搜索
Given many words, words[i] has weight i. Design a class WordFilter that supports one function, WordF ...
- [Swift]LeetCode745. 前缀和后缀搜索 | Prefix and Suffix Search
Given many words, words[i] has weight i. Design a class WordFilter that supports one function, WordF ...
- 【leetcode】745. Prefix and Suffix Search
题目如下: Given many words, words[i] has weight i. Design a class WordFilter that supports one function, ...
- leetcode@ [336] Palindrome Pairs (HashMap)
https://leetcode.com/problems/palindrome-pairs/ Given a list of unique words. Find all pairs of dist ...
- [Swift]LeetCode214. 最短回文串 | Shortest Palindrome
Given a string s, you are allowed to convert it to a palindrome by adding characters in front of it. ...
- 牛客国庆集训派对Day3 Solution
A Knight 留坑. B Tree 思路:两次树形DP,但是要考虑0没有逆元 可以用前缀后缀做 #include <bits/stdc++.h> using namespa ...
随机推荐
- TensorFlow中数据读取—如何载入样本
考虑到要是自己去做一个项目,那么第一步是如何把数据导入到代码中,何种形式呢?是否需要做预处理?官网中给的实例mnist,数据导入都是写好的模块,那么自己的数据呢? 一.从文件中读取数据(CSV文件.二 ...
- Netty学习之IO模型
目录 1.1 同步.异步.阻塞.非阻塞 同步 VS 异步 同步 异步 阻塞 VS 非阻塞 阻塞 非阻塞 举例 ...
- linux系统修改Swap分区【转】
在装完Linux系统之后自己去修改Swap分区的大小(两种方法) 在安装完Linux系统后,swap分区太小怎么办,怎么可以扩大Swap分区呢?有两个办法,一个是从新建立swap分区,一个是增加swa ...
- 我的程序员之路:自学Java篇
序章 时光疾驰,从事IT行业已两年有余. 16年11月开始自学Java,从此开启自学之路,后来实习期自学大数据.python.爬虫等,最终成长为一名平凡的程序员.回首望去,一路上的过往历历在目,有初学 ...
- FastApi学习(二)
前言 继续学习 此为第二篇, 还差些知识点就可以结束, 更多的比如用户的身份校验/ swagger 文档修改等以后会单独写 正文 使用枚举来限定参数 可以使用枚举的方式来限定参数为某几个值之内才通过 ...
- 解锁Renderbus客户端使用新技巧----快速渲染效果图篇
度娘说,效果图最基本的要求就是:应该符合事物的本身尺寸,不能为了美观而使用效果把相关模型的尺寸变动,那样的效果图不但不能起到表现设计的作用,反而成为影响设计的一个因素.可见高效渲染效果图是都是当下我们 ...
- Vue css过渡 和 js 钩子过渡
css过渡 <transition name="slide"> <div v-show="!showChatInput" class=&quo ...
- DTCC 2020 | 阿里云李飞飞:云原生分布式数据库与数据仓库系统点亮数据上云之路
简介: 数据库将面临怎样的变革?云原生数据库与数据仓库有哪些独特优势?在日前的 DTCC 2020大会上,阿里巴巴集团副总裁.阿里云数据库产品事业部总裁.ACM杰出科学家李飞飞就<云原生分布式数 ...
- 原生javascript制作省市区三级联动详细教程
多级联动下拉菜单是前端常见的效果,省市区三级联动又属于其中最典型的案例.多级联动一般都是与数据相关联的,根据数据来生成和修改联动的下拉菜单.完成一个多级联动效果,有助于增强对数据处理的能力. 本实例以 ...
- DHCP最佳实践(二)
这是Windows DHCP最佳实践和技巧的最终指南. 如果您有任何最佳做法或技巧,请在下面的评论中发布它们. 在本指南(二)中,我将分享以下DHCP最佳实践和技巧. 从DHCP作用域中排除IP 了解 ...