• NOIAC1755 Trie


    NOIAC1755 Trie

    题目大意

    给定一个字典(字符串的集合), 有若干次询问, 每次询问给出一个文本串 (S), 需要回答 (S) 是否有某个子串与字典内的某个单词(字符串)同构.
    一个串 (S) 与一个串 (T) 同构, 首先需要 (|S|=|T|), 并且存在一个关于字符集的双射 (f) 使得 (S_i = f(T_i)).
    比如 (ABB)(XYY, BAA, TSS) 同构, 但不和 (AAB, XXY, ZZZ) 同构.

    数据范围

    (N le 10^5, M le 5 imes 10^5)

    解题思路

    首先有个暴力的思路,即枚举每个子串,然后分配为字典序最小的串,哈希匹配一下即可。

    容易发现复杂度瓶颈在枚举子串而且不好优化。因此我们考虑如何用 AC 自动机来完成匹配的过程。

    我们可以记录每个字符当前位置和上一个出现位置的差,如果两个字符串的这个数组相同,即可认定两个字符串同构。因为字符集比较大,因此我们用 map 存暴力跳 fail 即可。另外要注意匹配时如果长度比上一个位置的差还要短,那么我们自动把它变为 i 即可,也就是匹配前缀是我们把 i 和 更大的数看成一个即可。

    代码

    /*
         />  フ
         |  _  _|
         /`ミ _x 彡
         /      |
        /   ヽ   ?
     / ̄|   | | |
     | ( ̄ヽ__ヽ_)_)
     \二つ
     */
    
    #include <queue>
    #include <vector>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define MP make_pair
    #define ll long long
    #define fi first
    #define se second
    using namespace std;
    
    template <typename T>
    void read(T &x) {
        x = 0; bool f = 0;
        char c = getchar();
        for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
        for (;isdigit(c);c=getchar()) x=x*10+(c^48);
        if (f) x=-x;
    }
    
    template<typename F>
    inline void write(F x, char ed = '
    ') {
    	static short st[30];short tp=0;
    	if(x<0) putchar('-'),x=-x;
    	do st[++tp]=x%10,x/=10; while(x);
    	while(tp) putchar('0'|st[tp--]);
    	putchar(ed);
    }
    
    template <typename T>
    inline void Mx(T &x, T y) { x < y && (x = y); }
    
    template <typename T>
    inline void Mn(T &x, T y) { x > y && (x = y); }
    
    #include <map>
    const int N = 1005000;
    #define init(p, c) (mp[(p)].find(c) != mp[(p)].end())
    char s[N]; map<int, int> mp[N]; int ed[N], dep[N], cnt;
    int las[30], d[N];
    void work(int len) {
    	memset(las, 0, sizeof(las));
    	for (int i = 1;i <= len; i++)
    		d[i] = i - las[s[i]-'A'], las[s[i]-'A'] = i;
    }
    
    void insert(int len) {
    	int p = 0;
    	for (int i = 1;i <= len; i++) {
    		int c = d[i];
    		if (!init(p, c)) mp[p][c] = ++cnt;
    		p = mp[p][c], dep[p] = i;
    	}
    	ed[p] = 1;
    }
    
    int f[N];
    void build(void) {
    	queue<int> q;
    	for (auto v: mp[0]) q.push(v.se);
    	while (q.size()) {
    		int x = q.front(); q.pop();
    		ed[x] |= ed[f[x]];
    		for (auto v: mp[x]) {
    			q.push(v.se); int fat;
    			for (fat = f[x]; fat && !init(fat, min(v.fi, dep[fat] + 1)); fat = f[fat]);
    			if (init(fat, min(v.fi, dep[fat] + 1))) f[v.se] = mp[fat][min(v.fi, dep[fat] + 1)];
    		}
    	}
    }
    
    bool check(int len) {
    	int p = 0;
    	for (int i = 1;i <= len; i++) {
    		for (; p && !init(p, min(dep[p] + 1, d[i])); p = f[p]);
    		if (init(p, min(dep[p] + 1, d[i]))) p = mp[p][min(dep[p] + 1, d[i])];
    		if (ed[p]) return 1;
    	}
    	return 0;
    }
    
    int m, n;
    int main() {
    	read(n);
    	for (int i = 1;i <= n; i++) {
    		scanf ("%s", s + 1);
    		int len = strlen(s + 1); 
    		work(len), insert(len);
    	}
    	build(), read(m);
    	for (int i = 1;i <= m; i++) {
    		scanf ("%s", s + 1);
    		int len = strlen(s + 1);
    		work(len), puts(check(len) ? "Yes" : "No");
    	}
    	return 0;
    }
    
  • 相关阅读:
    有7g和2g的砝码各一个,怎样称可以3次把140g东西分为50g和90g???????
    中缀到后缀(一个例子)
    动态代理模式的使用
    代理模式用来初始化的延迟下载
    ReentrantLock Condition 实现消费者生产者问题
    Two Sum
    [leetcode]重建二叉树(先序和终须) 中序遍和后续
    (转载)旋转数组查找 最简洁方法 总结
    [不明觉厉] 下一个排列
    codeforces -- 283A
  • 原文地址:https://www.cnblogs.com/Hs-black/p/13578676.html
Copyright © 2020-2023  润新知