Gym 100712L Alternating Strings II(单调队列)

题目链接 Alternating Strings II

题意是指给出一个长度为n的01串,和一个整数k,要求将这个01串划分为很多子串(切很多刀),使得每个子串长度不超过k,且每个字串不是01交替出现的串(例如01, 10, 101, 010, 101010这些都是01交替出现的串),求最少需要切多少次

 

令F[i]代表前i个数所需要切的最少的刀数(从1开始计数),那么有

        F[i]  = min{F[j] | |j + 1, i| <= k 且 [j, i]这个子串不是01交替出现的串} + 1

这时O(n^2)的思路,足以解决上一个问题Gym 100712D了(n=1000),但是这个题数据n<=100000,所以首先寻求NlogN的方法。

 

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

1.s[i] == s[i - 1] 这时候没有以i结尾交替出现的串,重置pre=1,f[i] = min{f[i - k], ..., f[i - 1]} + 1  (初始化f[0] = -1,注意f下标是从1开始计数的)

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

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

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

 下面是代码:

 1 //#pragma comment(linker, "/STACK:1677721600")
 2 #include <map>
 3 #include <set>
 4 #include <stack>
 5 #include <queue>
 6 #include <cmath>
 7 #include <ctime>
 8 #include <vector>
 9 #include <cstdio>
10 #include <cctype>
11 #include <cstring>
12 #include <cstdlib>
13 #include <iostream>
14 #include <algorithm>
15 using namespace std;
16 #define INF 0x3f3f3f3f
17 #define inf (-((LL)1<<40))
18 #define lson k<<1, L, (L + R)>>1
19 #define rson k<<1|1,  ((L + R)>>1) + 1, R
20 #define mem0(a) memset(a,0,sizeof(a))
21 #define mem1(a) memset(a,-1,sizeof(a))
22 #define mem(a, b) memset(a, b, sizeof(a))
23 #define FIN freopen("in.txt", "r", stdin)
24 #define FOUT freopen("out.txt", "w", stdout)
25 #define rep(i, a, b) for(int i = a; i <= b; i ++)
26 #define dec(i, a, b) for(int i = a; i >= b; i --)
27 
28 template<class T> T MAX(T a, T b) { return a > b ? a : b; }
29 template<class T> T MIN(T a, T b) { return a < b ? a : b; }
30 template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
31 template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b;    }
32 
33 //typedef __int64 LL;
34 typedef long long LL;
35 const int MAXN = 100000 + 100;
36 const int MAXM = 110000;
37 const double eps = 1e-8;
38 LL MOD = 1000000007;
39 const double PI = 4.0 * atan(1.0);
40 
41 int t, n, k;
42 char s[MAXN];
43 
44 struct SegTree {
45     int mi[MAXN << 2];
46 
47     void update(int k, int L, int R, int p, int v) {
48         if(L == R) { mi[k] = v; return ; }
49         if((L + R) / 2 >= p) update(lson, p, v);
50         else update(rson, p, v);
51         mi[k] = min(mi[k << 1], mi[k << 1 | 1]);
52     }
53 
54     int query(int k, int L, int R, int l, int r) {
55         if(l == 0) return -1;
56         if(R < l || r < L) return INF;
57         if(l <= L && R <= r) return mi[k];
58         return min(query(lson, l, r), query(rson, l, r));
59     }
60 
61 }st;
62 
63 int main()
64 {
65 //    FIN;
66     while(~scanf("%d", &t)) while(t--) {
67         scanf("%d %d%*c %s", &n, &k, s);
68         mem0(st.mi);
69         st.update(1, 1, n, 1, 0);
70         int pre = 1, val;
71         rep (i, 2, n) {
72             if(s[i - 1] == s[i - 2]) {
73                 pre = 1;
74                 val = st.query(1, 1, n, max(i - k, 0), i - 1);
75             }
76             else {
77                 pre ++;
78                 if(pre >= k || pre == i) val = st.query(1, 1, n, i - 1, i - 1);
79                 else val = st.query(1, 1, n, max(i - k, 0), i - 1 - pre);
80             }
81             st.update(1, 1, n, i, val + 1);
82         }
83         cout << st.query(1, 1, n, n, n) << endl;
84     }
85     return 0;
86 }
View Code

 

同时,这道题还有O(N)的方法(想了太久才搞出= =!)

在上面的过程中讲到,如果i可以由j转移得到,那么一定可以由j - 1转移得到(前提是|j - 1, i| <= k), 那么如果此时f[j - 1] > f[j],那么在计算f[i]时吗,只要j存在,就不可能由j - 1转移(这时显而易见的),所以我们使用队列维护一个f值不降的序列,并记录下标。

在计算f[i]时,首先将队首与i的区间长度>k的点删掉,然后再根据上面讨论的几种情况判断是根据队首转移还是队尾转移,然后将f[i]入队,再根据f[i]的值维护队列的单调性。

 1 //#pragma comment(linker, "/STACK:1677721600")
 2 #include <map>
 3 #include <set>
 4 #include <stack>
 5 #include <queue>
 6 #include <cmath>
 7 #include <ctime>
 8 #include <vector>
 9 #include <cstdio>
10 #include <cctype>
11 #include <cstring>
12 #include <cstdlib>
13 #include <iostream>
14 #include <algorithm>
15 using namespace std;
16 #define INF 0x3f3f3f3f
17 #define inf (-((LL)1<<40))
18 #define lson k<<1, L, (L + R)>>1
19 #define rson k<<1|1,  ((L + R)>>1) + 1, R
20 #define mem0(a) memset(a,0,sizeof(a))
21 #define mem1(a) memset(a,-1,sizeof(a))
22 #define mem(a, b) memset(a, b, sizeof(a))
23 #define FIN freopen("in.txt", "r", stdin)
24 #define FOUT freopen("out.txt", "w", stdout)
25 #define rep(i, a, b) for(int i = a; i <= b; i ++)
26 #define dec(i, a, b) for(int i = a; i >= b; i --)
27 
28 template<class T> T MAX(T a, T b) { return a > b ? a : b; }
29 template<class T> T MIN(T a, T b) { return a < b ? a : b; }
30 template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
31 template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b;    }
32 
33 //typedef __int64 LL;
34 typedef long long LL;
35 const int MAXN = 100000 + 100;
36 const int MAXM = 110000;
37 const double eps = 1e-8;
38 LL MOD = 1000000007;
39 
40 const int R = 0;
41 const int S = 1;
42 const int P = 2;
43 
44 struct Node {
45     int id, x;
46     Node(int _id = 0, int _x = 0) {
47         id = _id; x = _x;
48     }
49 }q[MAXN];
50 int st = 0, ed = 0;
51 int T, n, k;
52 char s[MAXN];
53 
54 void pop_fr(int i) {
55     while(st < ed && i - q[st].id > k) st++;
56 }
57 
58 void pop_ed(int id, int val) {
59     while(st < ed && q[ed - 1].x > val) ed --;
60     q[ed++] = Node(id, val);
61 }
62 
63 int main()
64 {
65 //    FIN;
66 //    FOUT;
67     int cas = 0;
68     while(~scanf("%d", &T)) while(T--) {
69         scanf("%d %d%*c %s", &n, &k, s);
70         int len = strlen(s), pre = 1;
71         int f = 0;
72         st = ed = 0;
73         q[ed ++] = Node(0, -1);
74         q[ed ++] = Node(1, 0);
75         rep (i, 1, len - 1) {
76             pop_fr(i + 1);
77             if(s[i] == s[i - 1]) {
78                 f = q[st].x + 1;
79                 pre = 1;
80             }
81             else {
82                 pre ++;
83                 f = (pre >= (i + 1 - q[st].id) || pre == i + 1) ? (q[ed - 1].x + 1) : (q[st].x + 1);
84             }
85             pop_ed(i + 1, f);
86             //printf("%d: %d\n", i + 1, f);
87         }
88         cout << f << endl;
89     }
90     return 0;
91 }

 

posted @ 2015-07-20 21:35  再见~雨泉  阅读(398)  评论(0编辑  收藏  举报