FZU - 2280 I - Magic (哈希||字典树)

版权声明:希望能在自己成长的道路上帮到更多的人,欢迎各位评论交流 https://blog.csdn.net/yiqzq/article/details/81910169

原题地址:http://acm.fzu.edu.cn/problem.php?pid=2280
题意:给你 n 个字符串,每个字符串有一个值 w ,有 q 次询问,一共两种操作:一是 1 , x , y 表示把第 x 个串的 w 变为 y ;二是“ 2 , x ,输出第 x 个串能放几次魔法。放魔法的条件是这样:用串x放魔法,如果在1~n个串中,一个串的 w 不超过 x w 并且 x 是这个串的后缀,则算放了一次魔法。

哈希写法

思路:用哈希值表示每一个字符串,然后用一个数组记录权值,一个数字记录字符串长度,每次询问就O(n)直接找就行了.

不得不吐槽一句这个OJ 真的毒瘤,一开始以为每次O(n)的遍历会T,然后set里面套了pair去二分查找,结果二分T了,然后发现直接遍历过了…….
下面注释掉的是本来二分的写法,希望有大佬能解释一下原因

//#include <bits/stdc++.h>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <set>
#include <map>
#include <cctype>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 1e5 + 5;
const int mod = 998244353;
typedef pair<int, int>pii; //第1维是权值,第2维是第几个字符串
int a[maxn];//记录第i个字符串的权值,方便后面删除
int T, n, q;
multiset<pii>P;
multiset<pii>::iterator it, iter;
char str[1005][1005];
ull p[maxn], h[1005][1005];
int length[maxn];
void Hash(int id, char *s) {//处理第id个字符串的哈希值
    h[id][0] = 0;
    for (int i = 1; i <= length[id]; i++) h[id][i] = h[id][i - 1] * seed + s[i];
}
ull get_has(int id, int l, int r) {//获得第id个字符串[l,r]区间的哈希值
    return h[id][r] - h[id][l - 1] * p[r - l + 1];
}
//int check(int x) {
//    iter = P.upper_bound(make_pair(a[x], 1001));
//    int ans = 0;
//    int len = strlen(str[x] + 1);
//    ull STD = get_has(x, 1, len);
//    for ( it = P.begin(); it != iter; it++) {
//        int mid = it->second;
//        int lenmid = strlsen(str[mid] + 1);
//        if (lenmid < len) continue;
//        ull my = get_has(mid, lenmid - len + 1, lenmid);
//        if (my == STD) ans++;
//    }
//    return ans;
//}

int check(int mid) {
    int ans = 0;
    ull STD = get_has(mid, 1, length[mid]);//所要匹配的后缀的哈希值
    for (int i = 1; i <= n; i++) {//直接O(n)找
        if (a[i] > a[mid])continue;
        if (length[i] < length[mid]) continue;
        ull my = get_has(i, length[i] - length[mid] + 1, length[i]);
        if (my == STD) ans++;
    }
    return ans;
}
int main() {
    p[0] = 1;
    for (int i = 1; i <= 1005; i++) p[i] = p[i - 1] * seed;//预处理
    scanf("%d", &T);
    while (T--) {
//        P.clear();
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; i++) {
            int val;
            scanf("%s%d", str[i] + 1, &val);
            length[i] = strlen(str[i] + 1);
            Hash(i, str[i]);
            a[i] = val;
//            P.insert(make_pair(val, i));
        }
        scanf("%d", &q);
        while (q--) {
            int op, x, y;
            scanf("%d", &op);
            if (op == 1) {
                scanf("%d%d", &x, &y);
//                P.erase(make_pair(a[x], x));
                a[x] = y;
//                P.insert(make_pair(a[x], x));
            } else {
                scanf("%d", &x);
                int ans = check(x);
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}

字典树+树状数组

大佬的思想:

思路:把字符串反转就转化为前缀的问题,这样就可以用字典树来处理是否为前缀问题
然后再用树状数组来维护数量 ,具体做法是把字符串按长度从小到大插入字典树 ,对于第i次插入, 若某个结点是结尾结点,则将该点的树状数组更新 ,因为以该点结尾的字符串是当前插入字符串的前缀 ,查询操作就是查询第k个字符串结尾的点的树状数组1到w[k]的和 ,修改操作同样和插入操作类似,把路径上结尾结点的所有树状数组更新 ,由于只有n个字符串,开n颗树状数组就够了
查询操作O(logN)
修改操作O(len logN)
总复杂度上界O(T * q * len * logN)

#include <cmath>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <set>
#include <map>
#include <cctype>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 1000 + 5;
const int mod = 998244353;
int T, n, q, pos;
int bit[maxn][maxn];//bit[i][j]第i种字符串,[1,j]的和
int trie[maxn][27], End[maxn]; //End[k]=p记录第k个字符串结尾是属于第p个树状数组
int length[maxn], w[maxn]; //length每个字符串的长度,w是权值
int id[maxn]; //id[root]=num,表示当前节点root是第num个字符串的结尾
char str[maxn][maxn];//记录每一个字符串
struct node {
    int id, len;
} e[maxn];
bool cmp(node a, node b) {
    return a.len < b.len;
}
int lowbit(int x) {
    return x & (-x);
}
void add(int k, int x, int num) { //第k个,在权值为w的上面加上num
    while (x <= 1000) {
        bit[k][x] += num;
        x += lowbit(x);
    }
}
int sum(int k, int  x) { //求第k个树状数组,前x项的和
    int ans = 0;
    while (x) {
        ans += bit[k][x];
        x -= lowbit(x);
    }
    return ans;
}


void Insert(int k, char *s) { //插入第k个字符串
    int root = 0;
    for (int i = length[k]; i >= 1; i--) {
        if (trie[root][s[i] - 'a'] == 0) {
            trie[root][s[i] - 'a'] = pos++;
        }
        root = trie[root][s[i] - 'a'];
        if (id[root]) {//因为是要求后缀相同,那么长度长的字符串对短的有影响
            add(id[root], w[k], 1);
        }
    }
    if (id[root] == 0) {
        id[root] = k;
        add(id[root], w[k], 1);
    }
    End[k] = id[root];//这么写是因为可能有重复
}
void updata(int k, int v, char *s) {
    int root = 0;
    for (int i = length[k]; i >= 1; i--) {
        root = trie[root][s[i] - 'a'];
        if (id[root]) {
            add(id[root], w[k], -1);//修改操作相当于减去原来的在加上更新的
            add(id[root], v, 1);
        }
    }
    w[k] = v;
}
int main() {
    scanf("%d", &T);
    while (T--) {
        pos = 1;
        CLR(id, 0);
        CLR(End, 0);
        CLR(trie, 0);
        CLR(bit, 0);
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%s%d", str[i] + 1, &w[i]);
            length[i] = strlen(str[i] + 1);
            e[i].id = i;
            e[i].len = length[i];
        }
        sort(e + 1, e + 1 + n, cmp);
        for (int i = 1; i <= n; i++) {
            Insert(e[i].id, str[e[i].id]);
        }
        scanf("%d", &q);
        while (q--) {
            int op, k, x;
            scanf("%d", &op);
            if (op == 2) {
                scanf("%d", &k);
                int ans = sum(End[k], w[k]);
                printf("%d\n", ans);
            } else {
                scanf("%d%d", &k, &x);
                updata(k, x, str[k]);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/81910169