HDU 6096 String(字典树)

/**
HDU 6096 String
题意:给出n个字符串,q个查询,每个查询包含A、B两个字符串,问在给定的n个字符串中,有多少个字符串
前缀是A,后缀是B且前缀后缀没有重叠部分

思路:对查询离线处理,给定的字符串保存下来,而对查询的前缀后缀建立字典树,建树过程如下,假设有
ac ef这种查询情况:
先将ef翻转过来变为ac fe, 之后再加一个特殊字符连接起来变为ac#fe,对ac#fe建立字典树,并在这个字符
串的结尾处设置一个值,这个值为当前查询前后缀的下标k(即第几个查询),如果查询的前缀后缀都相同的,
取第一个出现的就好了
然后是对给定的字符串查询,就是查询每个字符串对查询的贡献,假设有ac ef的查询,有给定的字符串acdef
在字典树上匹配的时候有先有0->a,发现没有a#,继续,然后0->a->c发现ac#有,那么就开始将acdef的字符串
反过来匹配了,变成0->a->c->#->f->e 因为f没有值,到达e的时候发现e这处节点有值k,那么就是ans[k]++
**/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<algorithm>
typedef long long ll;
const int maxn = 6e5 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
using namespace std;

int val[maxn], sz;
int ch[maxn][27];
char str[maxn];
char s[maxn];
int n, m, T, kase = 1, q;
int len[maxn], ans[maxn];
int nxt[maxn];

int __insert(int st, int l, int r, int v, int flag, int x) {
    int u = st, t = abs(r - l) + 1;
    int uu;
    for(int i = l; t--; i += flag) {
        int c = s[i] - 'a';
        if(!ch[u][c]) {
            memset(ch[sz], 0, sizeof ch[sz]);
            val[sz] = 0;
            ch[u][c] = sz++;
        }
        uu = u;
        u = ch[u][c];
    }
    if(v) {
        if(val[u] == 0)  val[u] = v;
        else nxt[v] = val[u];
    }
    return u;
}

void query(int L, int R) {
    int u = 0;
    for(int i = L; i <= R; i++) {
        int c = str[i] - 'a';
        if(!ch[u][c]) return ;
        u = ch[u][c];
        if(!ch[u][26]) continue;
        int st = ch[u][26];
        for(int j = R; j > i; j--) {
            int k = str[j] - 'a';
            st = ch[st][k];
            if(!st) break;
            if(val[st]) ans[val[st]]++;
        }
    }
}

int input() {
	int res = 0, ch, flag = 0;
	if((ch = getchar()) == '-') flag = 1; ///正负
	else if(ch >= '0' && ch <= '9') res = ch - '0';
	while((ch = getchar()) >= '0' && ch <= '9' ) res = res * 10 + ch - '0';
	return flag ? -res : res;
}

int main() {
    //scanf("%d", &T);
    T = input();
    while(T--) {
        sz = 1;
        memset(ch[0], 0, sizeof ch[0]);
        val[0] = 0;
       // scanf("%d %d", &n, &q);
        n = input(); q = input();
        for(int i = 1; i <= q; i++) nxt[i] = i;
        int num = 0;
        for(int i = 0; i < n; i++) {
            scanf("%s", str + num);
            len[i] = strlen(str + num);
            num += len[i];
        }
        for(int i = 1; i <= q; i++) {
            ans[i] = 0;
            scanf("%s", s);
            int l1 = strlen(s);
            s[l1++] = 'a' + 26;
            scanf("%s", s + l1);

            int l2 = strlen(s + l1);
            int node = __insert(0, 0, l1 - 1, 0, 1, i);
            __insert(node, l2 + l1 - 1, l1, i, -1, i);
        }
        num = 0;
        for(int i = 0; i < n; i++) {
            query(num, num + len[i] - 1);
            num += len[i];
        }
        for(int i = 1; i <= q; i++) {
            printf("%d\n", ans[nxt[i]]);
        }
    }
    return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值