zoukankan      html  css  js  c++  java
  • atcoder 2017Code festival C ——D题 Yet Another Palindrome Partitioning(思维+dp)

    题目大意:

    把一个字符串s分割成m个串,这m个串满足至多有一种字符出现次数为奇数次,其他均为偶数次,问m的最小值

    题解:

    首先我们想一下纯暴力怎么做

    显然是可以n^2暴力的,然后dp[i]表示分割到i的所用最少的串个数

    接下来就是枚举所有可行的j,使得dp[j]转移到dp[i]。

    虽然可以暴力找,但是如果使用暴力找,则后续就无法优化了,这里就用到了异或的思想

    用一个26位的int数组a[i]表示从1到i的状态,第j位为1代表这个字母出现了奇数次,反之为偶数次。

    那么区间[l, r]是否可行,就是a[l]^a[r]的值必须为2的幂次。

    有了这个性质,转移依然是n^2的,但是我们可以优化它。

    dp[i][x]表示1~i中当前状态为x,分割到i所用最少的串个数。

    那么考虑如何更新i+1,首先i+1的状态是固定的,就是a[i]。

    所以能更新的就是所有使得y^a[i]是2的幂次的y,dp[i][a[i]] = min(dp[i][a[i]], dp[i-1][y])。

    注意异或是可交换,那么只需要枚举2的幂次,然后和a[i]异或,就可以得到y了

    于是就这么神奇的做完了!

    复杂度O(26*n)

    题解很麻烦,但是代码量是很少的。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int maxn = 2e5 + 100;
    char str[maxn];
    int a[maxn];
    int dp[(1<<26)];
    int main()
    {
        cin>>str;
        int L = strlen(str), ans = 0;
        for(int i = 0; i < L; i++){
            ans ^= (1<<(str[i] - 'a'));
            a[i] = ans;
        }
        memset(dp, 1, sizeof(dp));
        for(int i = 0; i < L; i++){
            int ok = 0;
            for(int j = 0; j < 26; j++) if(a[i] == 0 || (a[i]^(1<<j)) == 0) { dp[a[i]] = 1; ok = 1; break; }
            if(ok) continue;
            for(int j = 0; j < 26; j++)
                dp[a[i]] = min(dp[a[i]^(1<<j)]+1, dp[a[i]]);
        }
        cout<<dp[a[L-1]]<<endl;
        return 0;
    }
  • 相关阅读:
    Elasticsearch 分词
    Elasticsearch:文档乐观锁控制 if_seq_no与if_primary_term
    调用javaAPI访问hive
    sqoop笔记
    hive学习
    添加用户到sudo组
    HTTP协议用的TCP但是只建立单向连接
    Hadoop基本操作
    Hadoop原理介绍
    sed用法
  • 原文地址:https://www.cnblogs.com/Saurus/p/7724509.html
Copyright © 2011-2022 走看看