2015 ACM Amman Collegiate Programming Contest
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 Kand 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 Kbits. 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: Nand K (1 ≤ K ≤ N ≤ 100,000), where Nis the length of the string and Kis the maximum length of a resulting string. The next line contains a string of Nbits (0 or 1). Output For each test case, print a single line with the minimum number of cuts
题意:给定一个长度为N的字符串,需要把它切割成满足题目中条件约束的子串,问最少切几刀。 约束条件:1 长度不超过K, 2 不能是相互交替的子串, 也就是说不存在长度>=2的子串,且总是满足si!=si-1。
思路:在[i,i+k-1]中, 如果存在[i,x]为非交替的子串, 那么[i,j] j>=x 就一定为非交替子串, 所以可以维护一个最小的x, 然后在[x+1,i+k]中找一个最小的切割数,则f[i]=mincuts+1; 使用线段树维护, 写代码前,,先想清楚
这个代码时间复杂度O(nlgn) 但是L题T了, D数据小一些,过掉了, 看了下题解说的也是这个时间复杂度, 难道里面还有一些不知道的优化, 先贴个代码,向大牛请教后再来更新。
事后更新: 这份代码使用G++ 可以过, 时间是1996MS, 差一点就超。
#include<bits/stdc++.h> using namespace std; #define MAXN 0x3f3f3f3f struct node { int l,r,value,x,head,tail,minx; node(){} }; char s[100010]; int f[100010]; node tree[400040]; void build(int t, int l, int r) { tree[t].l=l; tree[t].r=r; tree[t].value=MAXN; tree[t].x=MAXN; tree[t].minx=MAXN; tree[t].head=-1; tree[t].tail=-1; if (l==r) return; int m=(l+r)/2; build(t*2,l,m); build(t*2+1,m+1,r); } void update(int t, int l, int r, int x, int v) { if (l<=tree[t].l&&tree[t].r<=r) { tree[t].value=x; tree[t].minx=l; tree[t].head=tree[t].tail=v; return; } int m=(tree[t].l+tree[t].r)/2; if (l<=m) update(t*2,l,r,x,v); else update(t*2+1,l,r,x,v); tree[t].tail=tree[t*2+1].tail; tree[t].head=tree[t*2].head; tree[t].value=min(tree[t*2].value, tree[t*2+1].value); if (tree[t*2].value<tree[t*2+1].value) tree[t].minx=tree[t*2].minx; else tree[t].minx=tree[t*2+1].minx; tree[t].x=min(tree[t*2].x, tree[t*2+1].x); if (tree[t*2].tail==tree[t*2+1].head&&tree[t*2+1].head!=-1) tree[t].x=min(tree[t].x, m+1); } int queryx(int t, int l, int r) { if (l<=tree[t].l&&tree[t].r<=r) { return tree[t].x; } int m=(tree[t].l+tree[t].r)/2; if (r<=m) return queryx(t*2,l,r); else if (l>m) return queryx(t*2+1,l,r); else { int x=MAXN; x=min(queryx(t*2,l,m),queryx(t*2+1,m+1,r)); if (tree[t*2].tail==tree[t*2+1].head&&tree[t*2+1].head!=-1) x=min(x, m+1); return x; } } int query_min(int t, int l, int r, int &minx) { if (l<=tree[t].l&&tree[t].r<=r) { minx=tree[t].minx; return tree[t].value; } int m=(tree[t].l+tree[t].r)/2; if (r<=m) return query_min(t*2,l,r,minx); else if (l>m) return query_min(t*2+1,l,r,minx); else { int x1,x2,minx1,minx2; x1=query_min(t*2,l,m,minx1); x2=query_min(t*2+1,m+1,r,minx2); if (x1<x2) minx=minx1; else minx=minx2; return min(x1,x2); } } int main() { int T,n,k; //freopen("test.in","r",stdin); //freopen("test2.out","w",stdout); scanf("%d",&T); while(T--) { scanf("%d%d",&n,&k); scanf("%s",s+1); f[n+1]=-1; f[n]=0; for (int i=n-1;i>=1;i--) f[i]=n; build(1,1,n); update(1,n,n,0,s[n]-'0'); for (int i=n-1;i>=1;i--) update(1,i,i,MAXN,s[i]-'0'); for (int i=n-1;i>=1;i--) { int x=queryx(1,i,min(i+k-1,n)); //非交替的的最小下标 if (x!=MAXN) { int minx; if (x+1<=min(i+k-1,n)) { query_min(1,x+1,min(i+k-1,n),minx); //从x到结尾切割的最小值 f[i]=min(f[i],f[minx]+1); //从最小值处切开 } f[i]=min(f[i],f[min(i+k-1,n)+1]+1); } else { f[i]=f[i+1]+1; } update(1,i,i,f[i],s[i]-'0'); } printf("%d\n",f[1]); //system("pause"); } return 0; } /* 4 6 3 111000 5 2 11010 3 3 110 3 3 101 */
附一份大牛的代码: 代码长度短, 时间又快
#include <iostream> #include <assert.h> #include <stdlib.h> #include <time.h> #include <stdio.h> #include <vector> #include <set> #include <map> #include <queue> #include <stack> #include <string> #include <string.h> #include <cmath> #include <memory.h> #include <algorithm> using namespace std; typedef long long ll; int n, k, dp[100005], seg[400001], v[100005]; char s[100002]; int at, val; void update(int n, int s, int e){ if (at<s || at>e) return; if (s == e){ seg[n] = at; v[at] = val; return; } update(n * 2, s, (s + e) / 2); update(n * 2 + 1, (s + e) / 2 + 1, e); if (v[seg[n * 2]]<v[seg[n * 2 + 1]]) seg[n] = seg[n * 2]; else seg[n] = seg[n * 2 + 1]; } int l, r; int get(int n, int s, int e){ if (l>e || r<s) return 0; if (s >= l && e <= r) return seg[n]; int a = get(n * 2, s, (s + e) / 2); int b = get(n * 2 + 1, (s + e) / 2 + 1, e); if (a == 0)return b; if (b == 0)return a; if (v[a]<v[b]) return a; return b; } int main() { srand(time(0)); int t; scanf("%d", &t); while (t--){ scanf("%d%d", &n, &k); scanf("%s", s + 1); for (int i = 0; i<4 * n; ++i) seg[i] = 0; int next = 1e9; dp[n + 1] = 0; val = 0; at = n; update(1, 1, n + 1); for (int i = n; i > 0; --i){ dp[i] = 1 + dp[i + 1]; if (s[i + 1] == s[i]) next = i + 1; l = next; r = min(n + 1, i + k - 1); if (l <= r){ int x = get(1, 1, n + 1); dp[i] = min(dp[i], 1 + v[x]); } at = i - 1; val = dp[i]; update(1, 1, n + 1); } printf("%d\n", dp[1] - 1); } return 0; }