Codeforces Gym 100971M Decomposition into Good Strings DP+数据结构

题目大意:

定义有k个不同的字符的字符串为好字符串。现在给出一个字符串,求解对该字符串的每个前缀Si至少是多少个好字符串的连接,若不能由好字符串连接而成则输出-1。
例:k = 2
abac至少是ab和ac这两个好字符串的连接。
字符串长度<=2e5

做法:

一个比较直观的dp方程是这样的
f[i]表示Si的答案
f[i] = min(f[j] + 1) (j < i且Sj + 1…i是好字符串)
直接做时间复杂度是O(n2)。

但是可以通过使用数据结构来使时间复杂度降低到O(nlogn)。
先预处理对于每一个i,j的范围是多少使得Sj…i是好字符串,
把j的区间表示为l[i]和r[i]。从后往前可在O(n)时间内预处理出l[i]和r[i]。
接下来把问题转化为了求区间最小值,接着就可以使用线段树维护了。

代码:

Accepted 10012kb 109ms GNU G++ 4.9.2 2190Bytes
#include 
#include 
#include 
#include 

using namespace std;

const int maxn = 1e5 * 2 + 10;

char buf[maxn];

int cha[26];

int ql[maxn], qr[maxn];

void init(int len, int k)
{
    memset(qr, -1, sizeof qr);
    int l = len, ct = 0;
    for (int i = len ; i > 0 ; i--)
    {
        while(ct < k && l > 0)
        {
            if (!cha[buf[l] - 'a'])
                ct++;
            cha[buf[l] - 'a']++;
            --l;
        }
        if (ct == k)
            qr[i] = l + 1;
        if (--cha[buf[i] - 'a'] == 0)
            --ct;
    }
    memset(cha, 0, sizeof cha);

    memset(ql, -1, sizeof ql);
    l = len, ct = 0;
    for (int i = len ; i > 0 ; i--)
    {
        while(ct <= k && l > 0)
        {
            if (!cha[buf[l] - 'a'])
                ct++;
            cha[buf[l] - 'a']++;
            --l;
        }
        if (ct == k + 1)
            ql[i] = l + 2;
        else if (ct == k && l == 0)
            ql[i] = l + 1;
        if (--cha[buf[i] - 'a'] == 0)
            --ct;
    }
}

struct tree
{
    int num;
    int m;
}t[maxn * 4];

void build(int l, int r, int p)
{
    t[p].num = maxn;
    if (l == r)
        return;
    t[p].m = (l + r) / 2;
    build(l, t[p].m, 2 * p);
    build(t[p].m + 1, r, 2 * p + 1);
}

int query(int l, int r, int ll, int rr, int p)
{
    if (l == ll && r == rr)
        return t[p].num;
    if (ll > t[p].m)
        return query(t[p].m + 1, r, ll, rr, 2 * p + 1);
    else if (rr <= t[p].m)
        return query(l, t[p].m, ll, rr, 2 * p);
    return min(
        query(l, t[p].m, ll, t[p].m, 2 * p),
        query(t[p].m + 1, r, t[p].m + 1, rr, 2 * p + 1));
}

void modify(int l, int r, int p, int pp, int num)
{
    if (l == r)
    {
        t[p].num = num;
        return;
    }
    if (pp <= t[p].m)
        modify(l, t[p].m, 2 * p , pp, num);
    else
        modify(t[p].m + 1, r, 2*p + 1, pp, num);
    t[p].num = min(t[p].num, num);
}

int main()
{
    int k, len;
    scanf("%d%s", &k, buf + 1);
    len = strlen(buf + 1);
    init(len, k);
    build(0, len, 1);
    for (int i = 1 ; i <= len ; i++)
    {
        int ans = -1;
        if (ql[i] == 1)
            modify(0, len, 1, i, ans = 1);
        else if (ql[i] != -1)
        {
            ans = query(0, len, ql[i] - 1, qr[i] - 1, 1);
            //printf("%d\n", ans);
            if (ans != maxn)
                modify(0, len, 1, i, ++ans);
            else
                ans = -1;
        }
        printf("%d ", ans);
    }
    return 0;
}

你可能感兴趣的:(题解,codeforces,数据结构)