8633 回文划分

11 篇文章 0 订阅

8633 回文划分

Description

    我们说一个字符串是回文串,那么意味着这个串从两边读起来的字母都是一样的。例如racecar是回文串, 然而fastcar则不是。


    对一个串的划分意思是将一个串划分为若干个部分。例如,racecar可以划分为race 和car两部分。给出
一个串,要把这个串划分为若干个回文串,那么至少要把这个串划分为多少部分?

例如

    ‘racecar’已经是回文串,划分为1 个部分即可(这个部分就是racecar)。 ‘fastcar’ 需要被划分为七个部分 (‘f’, ‘a’, ‘s’, ‘t’, ‘c’, ‘a’, ‘r’)。根据回文串的定义,单个字母也是回文串。 ‘aaadbccb’ 分成可以被分为三个回文串 (‘aaa’, ‘d’, ‘bccb’)。找不到更少的划分方法。

输入格式

    输入的第一行是数字T,表示输入文件含有T个CASE。之后有T行,每行有一个长度不大于1000的字 符串,全部由小写字母组成,中间没有空格。

输出格式

    对于每个CASE,输出一个数字,表示对该字符串的回文串最小划分。

输入样例

  • 3
  • racecar
  • fastcar
  • aaadbccb

输出样例

  • 1
  • 7
  • 3

解题思路

    一开始看到这道题首先可能想到的会是穷举法解决,没错,穷举确实会保证题目正确性。那么,该怎样进行穷举,循环?递归?—-这里我一开始使用的是用递归的方法解决,当然结果毫无疑问是超时的。

    为什么超时?由题目可知,字符串的长度最长可以都达到1000;下面先看看我一开始使用递归的解法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int Min = 10000, len;
int judgeH(char str[], int s, int e)//判断该段字符串区间是否为回文串
{
    if((e-s)%2) 
    {
        e--;
        while(s!=e)
            if(str[s++]!=str[e--])
                return 0;
        return 1;
    }
    else
    {
        e--;
        while((s-e)!=1)
            if(str[s++]!=str[e--])
                return 0;
        return 1;
    }
}
void dfs(char str[], int note[], int s, int j)
{
    if(s == len)
    {
        if(Min > j)
            Min = j;
    }
    else
    {
        for(int i = s+1; i <= len; i++)
        {
            if(judgeH(str, s, i))
            { 
                note[j] = i;
                dfs(str, note, note[j], j+1);
            }
        }
    }
}
int main()
{
    int N, note[1000] = {0};
    char str[1001];
    cin >> N;
    while(N--)
    {
        cin >> str;
        len = strlen(str);
        dfs(str, note, 0, 0);
        cout << Min << endl;
        Min = 10000;
    }
    return 0;
 } 

    像这样的方法我们确实可以保证他的准确性,因为其对字符串搭配的所有情况都进行了一次遍历,并保留最分段的最小值,但这却有一个致命的缺陷—-时间复杂度高。

    观察这段代码可以知道它的时间复杂度是O(n!),无疑,这是一个相当糟糕的解法

    那么我们该如何取改进我们的程序,使它满足题目的需求呢?—-这时我想到了动态规划。利用递推遍历将每个从首字符到遍历到的字符位置的最优情况进行标记

    eg:str = “aba”;

  • 用一个数组note对str中每个字符对应位置最优值进行保存并设置note[0]=0
  • 开始i = 1, note[i] = note[i-1]+1 = note[0]+1 = 1, k = 1; 在k~i之间所有满足回文串的情况下,note[i] = min(note[i], note[k-1]+1); i++
  • 循环直到i==strlen(str);
  • 得到结果note[strlen(str)] = 1;
对应代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int judgeH(char str[], int s, int e)
{
    if((e-s)%2) 
    {
        e--;
        while(s!=e)
            if(str[s++]!=str[e--])
                return 0;
        return 1;
    }
    else
    {
        e--;
        while((s-e)!=1)
            if(str[s++]!=str[e--])
                return 0;
        return 1;
    }
}
int main()
{
    int N, note[1001] = {0};
    char str[1001];
    cin >> N;
    while(N--)
    {
        cin >> str;
        for(int i = 1; i <= strlen(str); i++)
        {
            note[i] = note[i-1]+1;
            for(int j = 1; j <= i; j++)
            {
                if(judgeH(str, j-1, i))
                {
                    note[i] = min(note[i], note[j-1]+1);
                }
            }
        }
        cout << note[strlen(str)] << endl;
    }
    return 0;
}

由此也可计算该解法的复杂度O = (n^2), 题目要求字符串长度最长为10^3,由此可得操作数为10^6, 我们以1000ms=10^8次操作数来计算,该解法运行所使用的最大时间为10ms,明显符合题目要求

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值