大致题意:
一个2e5的字符串STR,最多包含K个字符(K < 10) , 有Q个操作(<2e4) 。
1. 把字符串[L, R]的字符修改成x
2. 给一个K个字符的某个排列s,询问最少重复次数的s连接而成的长串的子序列包含STR
思路1:
因为要修改STR,所以要对STR进行处理,然后要在logn以下的复杂度完成查询操作。
可以注意到最少重复的次数就是STR中相邻的两个字符(ch1, ch2) 在排列s中的位置是ch2在ch1之前,ch2和ch1必然是有两个s串组成。所以可以维护STR相邻字符的对数。用cnt[ch1][ch2]矩阵维护相邻字符ch1ch2的对数。答案就是len(STR) - 相邻对数
由于有修改操作,必须用线段树维护,所以修改的复杂度在O(Qlogn*K*K),查询的复杂度是O(K*K).问题解决
思路2:
同样是维护cnt矩阵,但不用线段树维护,用set维护(http://codeforces.com/contest/610/submission/15053499), 由代码可以看出,当修改某一段字符的时候,暴力维护,然后[L, R]字符已经相同,没有相邻对数了,所以删去set中的[L, R],然后插入两个边界的相邻情况。
这样均摊复杂度是:每次修改操作最多在set里增加2个值,所以整个修改过程最多增加O(n) , 删除的个数不会多于增加的值个数,也是O(n).
所以总复杂度是O(Q * K * K + n) 比 方法一的O(Q * K * K + Qlogn* K * K) 优了logn * K * K级别。
方法一代码:(占用内存比较多,所以用了2倍空间的线段树)
//#pragma comment(linker, "/STACK:1024000000,1024000000") #include <iostream> #include <cstring> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #include <string> #include <vector> #include <cstdio> #include <ctime> #include <bitset> #include <algorithm> #define SZ(x) ((int)(x).size()) #define ALL(v) (v).begin(), (v).end() #define foreach(i, v) for (__typeof((v).begin()) i = (v).begin(); i != (v).end(); ++ i) #define reveach(i, v) for (__typeof((v).rbegin()) i = (v).rbegin(); i != (v).rend(); ++ i) #define REP(i,n) for ( int i=1; i<=int(n); i++ ) #define rep(i,n) for ( int i=0; i< int(n); i++ ) using namespace std; typedef long long ll; #define X first #define Y second #define PB push_back #define MP make_pair typedef long double ld; typedef pair<int, int> pii; template <class T> inline bool RD(T &ret) { char c; int sgn; if (c = getchar(), c == EOF) return 0; while (c != '-' && (c<'0' || c>'9')) c = getchar(); sgn = (c == '-') ? -1 : 1 , ret = (c == '-') ? 0 : (c - '0'); while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0'); ret *= sgn; return 1; } template <class T> inline void PT(T x) { if (x < 0) putchar('-') ,x = -x; if (x > 9) PT(x / 10); putchar(x % 10 + '0'); } const int N = 2e5 + 100; char s[N]; int n, Q, K; struct node { int cnt[10][10]; int lv, rv, lazy; }; struct SegmentTree { inline int get_id(int l,int r) { return (l + r) | (l != r); } node a[2 * N]; void up(int l, int r) { int mid = (l + r) >> 1; int rt = get_id(l, r), ls = get_id(l, mid), rs = get_id(mid + 1, r); rep(i, K) rep(j, K) a[rt].cnt[i][j] = a[ls].cnt[i][j] + a[rs].cnt[i][j]; a[rt].cnt[a[ls].rv][a[rs].lv] ++; a[rt].lv = a[ls].lv; a[rt].rv = a[rs].rv; } void down(int l, int r) { int mid = (l + r) >> 1; int rt = get_id(l, r), ls = get_id(l, mid), rs = get_id(mid + 1, r); if(a[rt].lazy == -1) return ; memset(a[ls].cnt, 0, sizeof(a[ls].cnt)); memset(a[rs].cnt, 0, sizeof(a[rs].cnt)); a[ls].lazy = a[ls].lv = a[ls].rv = a[rs].lazy = a[rs].lv = a[rs].rv = a[rt].lazy; a[rt].lazy = -1; } void build(int l, int r) { int rt = get_id(l, r); a[rt].lazy = -1; if(l == r) { a[rt].lv = a[rt].rv = s[l] - 'a'; return ; } int mid = (l + r) >> 1; build(l, mid); build(mid + 1, r); up(l, r); } void update(int L, int R, int val, int l, int r) { int rt = get_id(l, r); int mid = (l + r) >> 1; if( L <= l && r <= R) { memset(a[rt].cnt, 0, sizeof(a[rt].cnt)); a[rt].lazy = a[rt].lv = a[rt].rv = val; return ; } down(l, r); if(L <= mid) update(L, R, val, l, mid); if(R > mid) update(L, R, val, mid + 1, r); up(l, r); } void debug(int pos, int l, int r) { int rt = get_id(l, r); if( l == r ) { printf("(%c %c)\n", a[rt].lv + 'a', a[rt].rv + 'a'); return ; } down(l, r); int mid = (l + r) >> 1; if(pos <= mid) debug(pos, l, mid); else debug(pos, mid + 1, r); } }A; int main() { cin >> n >> Q >> K; scanf("%s", s + 1); A.build(1, n); REP(i, Q) { int op; RD(op); if(op == 1) { int l, r; char val[5]; RD(l), RD(r); scanf("%s", val); A.update(l, r, val[0] - 'a', 1, n); } else { scanf("%s", s); int ans = n; for(int i = 0; s[i]; i ++) for(int j = i + 1; s[j]; j ++) ans -= A.a[A.get_id(1, n)].cnt[s[i] - 'a'][s[j] - 'a']; printf("%d\n", ans); } // REP(i, n) A.debug(i, 1, n); } }
方法二代码:http://codeforces.com/contest/610/submission/15053499