大致题意:
一个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