codeforces #311 557E E. Ann and Half-Palindrome(trie+dp+dfs)

题目链接:

点击打开链接

题目大意:

给出一个字符串,问这个字符串的是半回文的子串的第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 );
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值