题目:http://codeforces.com/contest/557/problem/E
题意:定义半回文串:字符串前面一半的奇数位上的字符和后一边对应位置上的字符相同的字符串。给定长度|s|<=5000的字符串,求它的半回文子串中,字典序第K小的半回文子串。
分析:定义dp[i][j]表示区间[i,j]代表的子串是否为半回文串。然后n^2推出所有的半回文子串。将所有的半回文子串插入字典树,然后遍历一下字典树,找到正确的位置输出就行了。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 5555;
char s[maxn];
int n,k;
bool dp[maxn][maxn],hasFind;
int trie[maxn*maxn][2],cnt,root,cover[maxn*maxn],fa[maxn*maxn];
int newnode()
{
trie[cnt][0]=trie[cnt][1]=-1;
cover[cnt]=0;
return cnt++;
}
void Init()
{
cnt=0;
root=newnode();
}
void Insert(int start)
{
int cur=root;
for(int i=start;i<n;i++)
{
int index=s[i]-'a';
if(trie[cur][index]==-1)
trie[cur][index]=newnode();
fa[trie[cur][index]]=cur;
cur=trie[cur][index];
if(dp[start][i])
cover[cur]++;
}
}
void dfs(int cur)
{
if(hasFind)
return ;
k-=cover[cur];
if(k<=0)
{
hasFind=true;
int f=0;
while(cur)
{
s[f++]=(trie[fa[cur]][0]==cur?'a':'b');
cur=fa[cur];
}
for(int i=f-1;i>=0;i--)
putchar(s[i]);
return ;
}
for(int i=0;i<2;i++)
if(trie[cur][i]!=-1)
dfs(trie[cur][i]);
}
int main()
{
scanf("%s%d",s,&k);
n=strlen(s);
for(int i=n-1;i>=0;i--)
for(int j=i;j<n;j++)
dp[i][j]=(j-i<=4?(s[i]==s[j]):((s[i]==s[j])&&dp[i+2][j-2]));
Init();
for(int i=0;i<n;i++)
Insert(i);
dfs(root);
return 0;
}