题目链接:
题目大意:
给出一个字符串,问这个字符串的是半回文的子串的第k个是什么,半回文就是前一半的奇数位满足回文串的条件
题目分析:
这道题要求输出字典序第k大,那么自然而然想到要通过字典树去输出。
首先利用动态规划预处理出是半回文的子串,dp[i][j]代表从i到j的子串是否是半回文
当子串的长度小于等于4时,只要s[i]==s[j]那么就是半回文
其余情况,s[i]==s[j]&&dp[i+2][j-2] 才是半回文,顶多算个递推把
之后就是将符合条件的子串暴力的插入字典树,然后查找第k个字符串就很水了。。。字典树搞一搞就可以了
dp过程o(n^2),字典树插入o(N^2),输出的复杂度是字典树的规模
这道题感觉是一个水题的拼凑,注意输出时加入'\0',防止字符串抽风多输出,谨记。。。。。。。
输出就是dfs递归输出。。。基本功
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAX 5007
using namespace std;
char s[MAX];
char ans[MAX];
int k,n,cnt;
int dp[MAX][MAX];
bool check ( int i , int j)
{
int len = (j-i+2)/2;
for ( int t = i ; t < len ; t++ )
if ( t%2 == 0 && s[t] != s[j-t] )
return false;
return true;
}
struct Node
{
int ok;
Node* b[2];
Node()
:ok(0)
{
memset ( b , 0 , sizeof ( b ) );
}
}*root;
void add ( int i )
{
Node * u = root;
for ( int t = i ; t < n ; t++ )
{
if ( !(u->b[s[t]-'a']) )
u->b[s[t]-'a'] = new Node();
u = u->b[s[t]-'a'];
if ( dp[i][t] ) u->ok++;
}
}
bool flag = false;
void dfs ( Node* u , int d )
{
if ( flag ) return;
if ( u->ok )
{
cnt += u->ok;
//printf ( "%d %s\n" , cnt , ans );
if ( cnt >= k )
{
ans[d] = 0;
printf ( "%s\n" , ans );
flag = true;
return;
}
}
for ( int i = 0 ; i < 2 ; i++ )
{
if ( !(u->b[i]) ) continue;
ans[d] = (char)(i+'a');
dfs ( u->b[i] , d+1 );
}
}
void print ( int i , int j )
{
for ( int i = 0 ; i <= j ; i++ )
printf ( "%c" , s[i] );
puts("");
}
int main ( )
{
while ( ~scanf ( "%s" , s ) )
{
root = new Node();
n = strlen(s);
memset ( dp , 0 , sizeof ( dp ) );
for ( int i = 0 ; i < n ; i++ )
dp[i][i] = 1;
scanf ( "%d" , &k );
/*for ( int i = 0 ; i < n ; i++ )
for ( int j = i; j < n ; j++ )
{
if ( s[i] != s[j] ) continue;
if ( check ( i , j ) )
{
add ( i , j );
//print ( i , j );
}
}*/
for ( int i = 1 ; i < n ; i++ )
for ( int j = 0 ; j < n-i ; j++ )
{
if ( s[j] != s[j+i] ) continue;
if ( i <= 4 ) dp[j][i+j] = 1;
else if ( dp[j+2][i+j-2] )
dp[j][i+j] = 1;
}
for ( int i = 0 ; i < n ; i++ )
add ( i );
//cout << "YES" << endl;
cnt = 0;
flag = false;
dfs ( root , 0 );
}
}