411. Petya the Hero
Memory limit: 65536 kilobytes
output: standard
Petya has come back from the Berland-Birland War, and now he is fond of gathering his friends and narrating his heroic deeds. You have probably heard the story telling that Petya, being isolated, captured two Birland officers single-handed, than, using their clothes and having got to know the password, penetrated into the army base of the enemy, forced the controlling system out of action and helped Berland army to seize the base. This story was also heard by Colonel Kruglyakovsky, who was especially interested in a detail. That is the way Petya managed to find out the password for entering the army base with his poor knowledge of Birland language. Called by the colonel young hero explained, that although Birland speech wasn't clear to him, it wasn't too difficult to write it down. At first Petya interrogated the captives and wrote down the speech of each one as a string of latin letters. He knew that Birland valid passwords could be read the same way in either direction, i.e. they were palindromes. So he had to use the program, searching for the longest common substring of two strings, which was valid as a password. After hearing the answer, Colonel Kruglyakovsky declared, that this program could be very useful for interrogation captives and for decoding secret messages... As far as Petya certanly hadn't any program, he asked you for help.
'a'-
'z'). The length of each string doesn't exceed 2000 symbols. The strings contain at least one common letter.
sample input | sample output |
abacaba abracab | aca |
sample input | sample output |
abbab babbab | abba |
本题题意描述SJB。说了那么多没用的最后其实就是要 求两个字符串的最长公共回文子串的长度
具体描述如下:
给定两个字符串,s1,s2 且 1=<|s1|,|s2|<=2000;
求s1,s2的最长公共回文子串subs的长度
即
一。subs是s1的子串,subs也是s2的子串
二。subs是回文的,palindromic;
三。subs是所有子串中最长的
求|subs|;
这是暑假集训的一道题,当时没做出来,因为根本不会字符串的一些神器。今天在朋友的提醒下,弄出来了
我还不会后缀数组和height数组,我不知道用后缀数组神器能否O(n)解决,去看了下别人代码,好像需要O(n^2)解决
毕竟这题n=2000,如果可以O(n)解决,应该会给n=10^5吧。我不清楚,待我学会后缀数组神器后,再把这题过一遍。
我的方法是AC自动机+n^2判断回文
既然n=2000,n^2算法可以承受,也可以存储下,就n^2判断每个子串是否回文
方法是以每个串为中心向两端拓展,若回文,则palin[i][j]=true,遇到不回文break即可
(注意到这里palin[][]数组初始化时候是false情况,所以只需把所有回文情况找到,改为true即可)
同时,再以每2个字符串中间为中心,向两端拓展,若回文,则palin[i][j]=true,遇到不回文则break。
假设输入的字符串是T,S
这样子palin[i][j]表示的就是S中 i 到 j 这个子串是否回文
然后,把S的每一个子串放到AC自动机里,如果该子串是回文(通过palin数组判断),则字典树该结点权值为1,否则为0 (相信熟悉AC自动机的朋友都看得懂)
注意,这里不能把所有的子串都放到AC自动机里,因为这样子会爆内存,一开始我就那么做,一直没过
怎么办呢?分n次放入就可以了
比如,S=s0s1s2....sn-1
第一次取s0...sn-1这一段,放入自动机里,然后用T进行AC自动机搜索,判断是否有一个回文子串是T的子串
第二次取s1...sn-1这一段,重复上述过程
......
最后一次取sn-1...sn-1这一段,其实只有一个字符,重复上述过程
不断维护子串的最长长度,同时记录位置即可。(题目保证一定有解)
代码如下:
//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN 10100
#define N 2014
#define M 26
bool palin[N][N];
void Palin_ornot(char *s,int len){
int t=1;
rep(i,0,len){
t=0;
while(i-t>=0 && i+t<len && s[i-t]==s[i+t]){
palin[i-t][i+t]=true;
t+=1;
}
}
int t1=0,t2=0;
rep(i,0,len-1){
t1=i,t2=i+1;
while(t1>=0 && t2<len && s[t1]==s[t2]){
palin[t1][t2]=true;
--t1,t2++;
}
}
}
struct Trienode{
int from,to,len;
}trienode[MAXN];
int trie[MAXN][M],num_node,val[MAXN];
int lenup,lendown;
char up[N],down[N];
int idx(char c){
return c-'a';
}
void init_Trie(){
clr(trie[0]);
num_node=1;
}
void plant_trie(char *s,int from,int to){
int now=0,letter;
rep(i,from,to){
letter=idx(s[i]);
if(!trie[now][letter]){
trie[now][letter]=num_node;
val[num_node]=palin[from][i]?1:0;
trienode[num_node].from=from;
trienode[num_node].to=i;
trienode[num_node].len=i-from+1;
clr(trie[num_node]);
num_node+=1;
}
now=trie[now][letter];
}
}
int nextpos[MAXN],lastpos[MAXN];
queue<int>q;
void Build_nextandlastpos(){
clr_queue(q);
nextpos[0]=lastpos[0]=0;
int now=0,son=0;
rep(i,0,M){
son=trie[now][i];
if(son){
nextpos[son]=lastpos[son]=0;
q.push(son);
}
}
int nextone;
while(!q.empty()){
now=q.front();
q.pop();
rep(i,0,M){
son=trie[now][i];
nextone=nextpos[now];
if(!son){
trie[now][i]=trie[nextone][i];
continue;
}
while(nextone && !trie[nextone][i]) nextone=nextpos[nextone];
nextpos[son]=trie[nextone][i];
lastpos[son]=val[nextpos[son]]?nextpos[son]:lastpos[nextpos[son]];
q.push(son);
}
}
}
int anst,from,to;
void Caculate_ans(int x){
if(x){
if(trienode[x].len>anst){
anst=trienode[x].len;
from=trienode[x].from;
to=trienode[x].to;
}
}
}
void AC_search(char *s,int len){
int now=0,letter=0;
rep(i,0,len){
letter=idx(s[i]);
now=trie[now][letter];
if(val[now]) Caculate_ans(now);
else if(lastpos[now]) Caculate_ans(lastpos[now]);
}
}
int main()
{
scanf("%s %s",up,down);
lenup=len(up);
lendown=len(down);
Palin_ornot(down,lendown);
anst=0;
rep(i,0,lendown){
init_Trie();
plant_trie(down,i,lendown);
Build_nextandlastpos();
AC_search(up,lenup);
}
repin(i,from,to){
printf("%c",down[i]);
}
printf("\n");
}