Hihocoder1690 AEIOU (动态规划)

分析

题目要求:
1. 所有的a都在e和i之前,所有的e都在i之前;
2. 所有的o都在u之前。

仔细分析发现如下特点:
* 其实aeia、e、i这三个字符和ouo、u这两个字符毫无关系,设给定字符串为SS,那么我只要从字符串S中选择只包含aeia、e、i且满足规则1的最长子序列,再从字符串SS中选择只包含ou且满足规则2的最长子序列,再将这两个字符串组合就得到满足规则最长子序列。

具体实现

根据上述分析,发现可以将原问题分成两个小问题来解决。

假设给定字符串"aeieeeeiaa""aeieeeeiaa",如何选择满足所有的a都在e和i之前,所有的e都在i之前的最长子序列呢?最长序列其实就是三段式:"a...ae...ei...i""a...ae...ei...i"aa一定在e前面,ee一定在i前面。用maxa,maxe,maximaxa,maxe,maxi分别表示字符串SSi个字符组成的子串中能得到的最长以"a,e,i""a,e,i"结尾的最长序列。

那么得到如下转移方程:

maxa=maxa+1maxa=maxa+1,因为a的前面只能是a

maxe=max{maxa,maxe}+1maxe=max{maxa,maxe}+1,因为e的前面可以是a或者e

maxi=max{maxa,maxe,maxi}+1maxi=max{maxa,maxe,maxi}+1,因为i的前面可以是a、e或者i

同样的方式,可以得到由oou组成的满足“所有的o都在u之前”的最长子序列。

AC代码

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 1000000 + 5;

char s[maxn];
int dp[5];  // a e i o u

int main() {
    while(scanf("%s", s) != EOF) {
        memset(dp, 0, sizeof(dp));
        int n = strlen(s);
        for(int i = 0; i < n; i++) {
            if(s[i] == 'a') {
                //the previous charactor must be 'a'
                dp[0] = dp[0] + 1;
            } else if(s[i] == 'e') {
                //the previous charactor must be 'a' or 'e'
                dp[1] = max(dp[0] + 1, dp[1] + 1);
            } else if(s[i] == 'i') {
                //the previous charactor must be 'a' or 'e' or 'i'
                int tmp = max(dp[0] + 1, dp[1] + 1);
                dp[2] = max(tmp, dp[2] + 1);
            } else if(s[i] == 'o') {
                //the previous charactor must be 'o'
                dp[3] = dp[3] + 1;
            } else {
                //the previous charactor must be 'o' or 'u'
                dp[4] = max(dp[3] + 1, dp[4] + 1);
            }
        }
        int max1 = max(dp[0], dp[1]);
        max1 = max(dp[2], max1);
        int max2 = max(dp[3], dp[4]);
        printf("%d\n", max1 + max2);
    }
    return 0;
}

如有不当之处欢迎指出!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值