这是一道如果没想清楚就不要乱打的题目,
否则就像我一样~~
题目描述
字符串A有N个子串B1,B2,…,Bn。如果将这n个子串分别放在恰好一个它在A中出现的位置上(子串之间可以重叠)这样A中的若干字符就被这N个子串覆盖了。问A中能被覆盖字符个数的最小值和最大值。
输入输出格式
输入格式:
第一行包含一个正整数T,表示数据组数。保证T<=10。
接下来依次描述T组数据,每组数据中:
第一行包含一个由小写字母组成的字符串,表示母串A。
第二行包含一个整数N,表示子串的个数。
接下来N行,每行包含一个由小写字母组成的字符串,描述子串。数据保证所有子串均在母串中出现。
输出格式:
输出为T行,对应每组数据的答案。每行包含两个整数Minans和Maxans,分别表示对应数据中能被覆盖字符数量的最小值和最大值。
输入输出样例
输入样例#1: 复制
2
hello
4
he
l
l
o
abacaba
4
ab
ba
a
c
输出样例#1: 复制
4 5
4 6
说明
字符串长度A<=10000,N<=4,子串长充<=1000
思考了接近一个小时,我想到了一种很难打的dp
设dp[i][S][j]表示到了原串的第i个位置,已经选了集合的状态为S,右端点最远覆盖到j。
这样就可以分情况讨论转移
其中now代表以i位置为左端点的串是哪个,r代表当前串的右端点
1.r’ < i时,dp[i][S | (1 << now)][r] = min{dp[j][s][r’] + (r - i + 1)}(1 <= j <= i-1)
2.i<= r’ < r时,dp[i][S | (1 << now)][r] = min{dp[j][s][r’] + r-r’}(1 <= j <= i-1)
3.r’ > i时,dp[i][S | (1 << now)][r’] = min{dp[j][s][r’]}(1 <= j <= i-1)
于是我们可以发现可以以每一个状态建立一棵线段树方便转移
至于有哪些串以当前位置为左端点可以kmp预处理,再用vector保存
但是我们发现这样会有一个严重的问题,就是当出现第3种情况时
也就是r’ > i时,转移到的状态最右端点变成了r’,而如果是这样,就意味着
我们需要枚举r’,这样复杂度突然就多了一个n,是无法承受的
事实上,这种情况,也就是区间互相包含的情况,需要特殊处理
我们在开始还要建立一棵线段树,预处理出每一个区间里所有包含的区间的二进制表示,也就是上面的now变成了多个串的二进制表示,这样最小值就可以直接转移了
但是最大值又会有问题,因为如果(S&now)不为零,那么就无法转移,结果会变大。所以只有在S&now不为零的时候转移,但又要考虑所有状态,所以还要枚举now的子集转移,复杂度变成了3^n *n *log2(n),常数巨大,还要卡一下常数。
如果你已经懂了思想,就自己写吧。
但如果有兴趣,也不妨看看我8.2k的代码
#include
#define ll long long
#define mm(a, b) memset(a, b, sizeof(a))
#define inf 999999999
#define For(i, a, b) for(int i = (a);i <= (b); ++i)
#define rep(i, a, b) for(int i = (a);i >= (b); --i)
#define gnn cerr << "GNN睡着了" << endl;
using namespace std;
int read(){
int sum = 0, fg = 1;
char c = getchar();
while(c < '0' || c > '9'){if(c == '-')fg = -1;c = getchar();}
while(c >='0' && c <='9')sum = (sum << 1) + (sum << 3) + c-'0', c = getchar();
return sum * fg;
}
const int maxn = 50010;
void file(){
#ifndef ONLINE_JUDGE
freopen("Thunder.in", "r", stdin);
freopen("Thunder.out", "w", stdout);
#endif
}
char a[maxn], ss[10][maxn];
int n, Next[maxn], len[maxn];
vector<int> pos[maxn], ans[maxn];
void Get(){
scanf("%s", a+1);
n = read();
For(i, 1, n){
scanf("%s", ss[i] + 1);
len[i] = strlen(ss[i] + 1);
}
len[0] = strlen(a+1);
}
void Next_Done(int h){
int k = 0;
Next[1] = 0;
For(i, 2, len[h]){
while(k && ss[h][k+1] != ss[h][i])k = Next[k];
if(ss[h][k+1] == ss[h][i]) ++k;
else k = 0;
Next[i] = k;
}
}
void Solve(int h){
int j = 0;
For(i, 1, len[0]){
while(j && ss[h][j+1] != a[i])j = Next[j];
if(ss[h][j+1] == a[i]){
++ j;
if(j == len[h]){
pos[i - len[h] + 1].push_back(h);
j = Next[j];
}
}
else j = 0;
}
}
void pre_work_kmp(){
For(i, 1, n){
For(j, 0, len[0] + 1) Next[j] = 0;
Next_Done(i);
Solve(i);
}
}
int fugai[maxn];
int NOWANS;
void insert_fugai(int h,int l,int r,int loc,int now){
if(l == r){
fugai[h] |= (1 << now-1);
return;
}
int mid = l+r >> 1;
if(loc <= mid) insert_fugai(h << 1, l, mid, loc, now);
else insert_fugai(h << 1 | 1, mid+1, r, loc, now);
fugai[h] = (fugai[h << 1] | fugai[h << 1 | 1]);
}
int query_fugai(int h,int l,int r,int s,int e){
if(l == s && r == e) return fugai[h];
int mid = l+r >> 1;
if(e <= mid) return query_fugai(h << 1, l, mid, s, e);
else if(s > mid) return query_fugai(h << 1 | 1, mid+1, r, s, e);
else return (query_fugai(h << 1, l, mid, s, mid) | query_fugai(h << 1 | 1, mid+1, r, mid+1, e));
}
void print(int h){
For(i, 1, n) if(h & (1 << i-1)) printf("1"); else printf("0"); puts("");
}
void pre_work_fugai(){
rep(i, len[0], 1){
for(int j = 0;j < pos[i].size(); ++j){
int v = pos[i][j];
int R = i + len[v] - 1;
insert_fugai(1, 1, len[0], R, v);
}
for(int j = 0;j < pos[i].size(); ++j){
int v = pos[i][j];
int R = i + len[v] - 1;
int Now = (query_fugai(1, 1, len[0], i, R) | (1 << v-1));
ans[i].push_back(Now);
}
}
}
int tree_one[(1 << 4) + 10][maxn << 2], tree_two[(1 << 4) + 10][maxn << 2], dp[(1 << 4) + 10][maxn];
void pre_work(){
For(i, 0, len[0]) ans[i].clear(), pos[i].clear();
mm(fugai, 0);
pre_work_kmp();
pre_work_fugai();
}
void insert_one(int h,int l,int r,int S,int loc,int val){
if(l == r){
tree_one[S][h] = min(tree_one[S][h], val);
return;
}
int mid = l+r >> 1;
if(loc <= mid) insert_one(h << 1, l, mid, S, loc, val);
else insert_one(h << 1 | 1, mid+1, r, S, loc, val);
tree_one[S][h] = min(tree_one[S][h << 1], tree_one[S][h << 1 | 1]);
}
void query_one(int h,int l,int r,int s,int e,int S){
if(tree_one[S][h] >= NOWANS) return;
if(l == s && r == e){
NOWANS = tree_one[S][h];
return ;
}
int mid = l+r >> 1;
if(e <= mid) query_one(h << 1, l, mid, s, e, S);
else if(s > mid) query_one(h << 1 | 1, mid+1, r, s, e, S);
else return query_one(h << 1, l, mid, s, mid, S), query_one(h << 1 | 1, mid+1, r, mid+1, e, S);
}
void insert_two(int h,int l,int r,int S,int loc,int val){
if(l == r){
tree_two[S][h] = min(tree_two[S][h], val - l);
return;
}
int mid = l+r >> 1;
if(loc <= mid) insert_two(h << 1, l, mid, S, loc, val);
else insert_two(h << 1 | 1, mid+1, r, S, loc, val);
tree_two[S][h] = min(tree_two[S][h << 1], tree_two[S][h << 1 | 1]);
}
void query_two(int h,int l,int r,int s,int e,int S){
if(tree_two[S][h] >= NOWANS) return;
if(l == s && r == e){
NOWANS = tree_two[S][h];
return ;
}
int mid = l+r >> 1;
if(e <= mid) query_two(h << 1, l, mid, s, e, S);
else if(s > mid) query_two(h << 1 | 1, mid+1, r, s, e, S);
else return query_two(h << 1, l, mid, s, mid, S), query_two(h << 1 | 1, mid+1, r, mid+1, e, S);
}
int ls[(1 << 4) + 10][maxn << 2];
void solve_min(){
int tmp = (1 << n) - 1;
for(int i = 0;i <= tmp; ++i) for(int j = 0;j <= len[0]; ++j) dp[i][j] = inf;
for(int i = 0;i <= tmp; ++i) for(int j = 0;j <= len[0] * 4; ++j) tree_one[i][j] = tree_two[i][j] = inf;
dp[0][0] = 0;
insert_one(1, 0, len[0], 0, 0, 0);
insert_two(1, 0, len[0], 0, 0, 0);
int Min = inf;
For(i, 1, len[0]){
For(j, 0, tmp - 1){
for(int k = 0;k < pos[i].size(); ++k){
int v = ans[i][k];
int R = i + len[pos[i][k]] - 1;
ls[j][k] = dp[j | v][R];
if((j | v) == j) continue;
int nowval = inf;
NOWANS = inf;
query_one(1, 0, len[0], 0, i-1, j);
nowval = min(nowval, NOWANS + len[pos[i][k]]);
NOWANS = inf;
query_two(1, 0, len[0], i, R, j);
nowval = min(nowval, NOWANS + R);
dp[j | v][R] = min(dp[j | v][R], nowval);
if(dp[j | v][R] < ls[j][k] && dp[j | v][R] < inf / 2){
insert_one(1, 0, len[0], (j | v), R, dp[j | v][R]);
insert_two(1, 0, len[0], (j | v), R, dp[j | v][R]);
}
}
}
Min = min(Min, dp[tmp][i]);
}
printf("%d ", Min);
}
void _insert_one(int h,int l,int r,int S,int loc,int val){
if(l == r){
tree_one[S][h] = max(tree_one[S][h], val);
return;
}
int mid = l+r >> 1;
if(loc <= mid) _insert_one(h << 1, l, mid, S, loc, val);
else _insert_one(h << 1 | 1, mid+1, r, S, loc, val);
tree_one[S][h] = max(tree_one[S][h << 1], tree_one[S][h << 1 | 1]);
}
void _query_one(int h,int l,int r,int s,int e,int S){
if(tree_one[S][h] < NOWANS) return;
if(l == s && r == e){
NOWANS = tree_one[S][h];
return;
}
int mid = l+r >> 1;
if(e <= mid) _query_one(h << 1, l, mid, s, e, S);
else if(s > mid) _query_one(h << 1 | 1, mid+1, r, s, e, S);
else return _query_one(h << 1, l, mid, s, mid, S), _query_one(h << 1 | 1, mid+1, r, mid+1, e, S);
}
void _insert_two(int h,int l,int r,int S,int loc,int val){
if(l == r){
tree_two[S][h] = max(tree_two[S][h], val - l);
return;
}
int mid = l+r >> 1;
if(loc <= mid) _insert_two(h << 1, l, mid, S, loc, val);
else _insert_two(h << 1 | 1, mid+1, r, S, loc, val);
tree_two[S][h] = max(tree_two[S][h << 1], tree_two[S][h << 1 | 1]);
}
void _query_two(int h,int l,int r,int s,int e,int S){
if(tree_two[S][h] < NOWANS) return;
if(l == s && r == e){
NOWANS = tree_two[S][h];
return;
}
int mid = l+r >> 1;
if(e <= mid) _query_two(h << 1, l, mid, s, e, S);
else if(s > mid) _query_two(h << 1 | 1, mid+1, r, s, e, S);
else return _query_two(h << 1, l, mid, s, mid, S), _query_two(h << 1 | 1, mid+1, r, mid+1, e, S);
}
void solve_max(){
int tmp = (1 << n) - 1;
for(int i = 0;i <= tmp; ++i) for(int j = 0;j <= len[0]; ++j) dp[i][j] = -inf;
for(int i = 0;i <= tmp; ++i) for(int j = 0;j <= len[0] * 4; ++j) tree_one[i][j] = tree_two[i][j] = -inf;
dp[0][0] = 0;
_insert_one(1, 0, len[0], 0, 0, 0);
_insert_two(1, 0, len[0], 0, 0, 0);
int Max = -inf;
For(i, 1, len[0]){
For(j, 0, tmp){
for(int k = 0;k < pos[i].size(); ++k){
int v = ans[i][k];
int R = i + len[pos[i][k]] - 1;
if(j & (1 << pos[i][k]-1)) continue;
int nowval = 0;
NOWANS = -inf;
_query_one(1, 0, len[0], 0, i-1, j);
nowval = max(nowval, NOWANS + len[pos[i][k]]);
NOWANS = -inf;
_query_two(1, 0, len[0], i, R, j);
nowval = max(nowval, NOWANS + R);
int now = (v ^ (1 << pos[i][k]-1));
int T = (j | (1 << pos[i][k]-1));
int Just = dp[T][R];
dp[T][R] = max(dp[T][R], nowval);
if(dp[T][R] > Just){
_insert_one(1, 0, len[0], T, R, dp[T][R]);
_insert_two(1, 0, len[0], T, R, dp[T][R]);
}
for(int o = now;o ;o = ((o-1)&now)){
Just = dp[T | o][R];
dp[T | o][R] = max(dp[T | o][R], nowval);
if(dp[T | o][R] > Just){
_insert_one(1, 0, len[0], (T | o), R, dp[T | o][R]);
_insert_two(1, 0, len[0], (T | o), R, dp[T | o][R]);
}
}
}
}
Max = max(Max, dp[tmp][i]);
}
printf("%d\n", Max);
}
int main(){
file();
int _ = read();
while(_ --){
Get();
pre_work();
solve_min();
solve_max();
}
return 0;
}