2015 ACM Amman Collegiate Programming Contest L .Alternating Strings II

L. Alternating Strings II
This problem is the same as problem D but with different constraints!
Bahosain has a strange habit. He writes his daily notes in binary! His little brother also has a strange habit. He
hates seeing alternating patterns of 0s and 1s or strings longer than

K

. Therefore, whenever Bahosain writes a
binary note on a paper, his little brother cuts it at night with scissors in order to make each part of it not
longer than

K

and not alternating.
After several years of suffering from cutting binary notes with scissors, Bahosain decided to cut his notes in
such a way that his brother won’t touch them.
Given a binary string of length

N

, find the minimum number of cuts Bahosain has to make such that each of
the resulting strings are NOT alternating and contains no more than

K

bits.
A binary string is considered alternating only if each bit after the first one is different from the one before it.
For example: strings 110, 0110 and 010100 are not alternating, while 101 and 01 are alternating strings.
A string with one character is not an alternating string.
Input
The first line of input contains

T (1  ≤ T  ≤ 128)

that represents the number of test cases.
The first line of each test case contains two integers:

N


and

K (1  ≤ K  ≤ N  ≤ 100,000)

, where

N

is the length
of the string and

K

is the maximum length of a resulting string.
The next line contains a string of

N

bits (0 or 1).
Output
For each test case, print a single line with the minimum number of cuts.
Sample Input 
4
6 3
111000
5 2
11010
3 3
110
3 3

101

 Sample Output

1
3
0

2


解法:(find)我们记录以i结尾的且是交替串的最长长度为pre,可以看到在计算f[i]时,有以下几种情况:

1.s[i] == s[i - 1] 这时候没有以i结尾交替出现的串,重置pre=1,f[i] = min{f[i - k], ..., f[i - 1]} + 1 }

2.s[i] != s[i - 1] 这时候pre++,由于最后一个与前一个形成交替串,所以需要比较pre与k的关系:

   a. 若pre >= k 或 pre == i,着说明连续出现的交替串长度>k,或者前i个全是01串,那么此时f[i]只能由f[i - 1]转移(相当于在i-1与i之间切一刀)

   b.否则的话pre + 1这个串([i - pre - 1, i])一定不是交替串,那么此时[i - k, i - pre - 1]这个区间的所有点都可以转移到i(最后pre+1个不是交替串,那么最后pre+1+x个一定也不是),所以有f[i] = min{f[i - k], ..., f[i - pre - 1]} + 1,所以可以使用线段树查询区间最小值,复杂度NlogN

总结:对于10^5一般对应于NlogN的算法。对于区间查询类的dp,我们可以考虑用线段树来优化。线段树的id必须从1开始,区间可以从0-n。

AC:

#include <stdio.h>
#include <algorithm>
#include<iostream>
using namespace std;
struct s
{
int left;
int right;
int mi;
}tree[800008];
char  s[800008];
void build(int id,int l,int r)
{
tree[id].left=l;
tree[id].right=r;
if(l==r)
{
tree[id].mi=0;
}
else{
int mid=(l+r)/2;
build(2*id,l,mid);
build(2*id+1,mid+1,r);
tree[id].mi = min(tree[id*2].mi,tree[2*id+1].mi);
}
}
void update(int id,int x,int y)
{
if(tree[id].left ==tree[id].right  )
{
tree[id].mi =y;
}
else
{
int mid=(tree[id].left +tree[id].right )/2;
if(x>mid) update(2*id+1,x ,y);
else update(2*id,x,y);
tree[id].mi =min(tree[2*id].mi,tree[2*id+1].mi);
}
}
int  query(int id,int l,int r)
{


if(tree[id].left >=l&&tree[id].right <=r)
return tree[id].mi;
else
{
int mid=(tree[id].left +tree[id].right )/2;
if(l>mid) return query(id*2+1,l,r);
else if(r<=mid)  return query(id*2,l,r);
        else return min(query(id*2+1,l,r),query(id*2,l,r));
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
    {
        int pre,val,n,k;
        pre=1;
        scanf("%d%d",&n,&k);
        scanf("%s",s+1);
        build(1,0,n);
        update(1,1,1);
        for(int i=2;i<=n;i++)
        {
            if(s[i]==s[i-1])
            {
                pre=1;
                val=query(1,max(i-k,0),i-1);
            }
            else {
                pre++;
                if(pre>=k||pre==i)
                {
                    val=query(1,i-1,i-1);
                }
                else val=query(1,max(i-k,0),i-1-pre);


            }
            update(1,i,val+1);
        }
        printf("%d\n",query(1,n,n)-1);
    }
}

你可能感兴趣的:(2015 ACM Amman Collegiate Programming Contest L .Alternating Strings II)