• 「LOJ #2102」「TJOI2015」弦论


    Description

    对于一个给定长度为 (n) 的字符串,求它的第 (k) 小子串。

    输入给定一个正整数 (T),若 (T = 0) 则表示不同位置的相同子串算作一个,(T = 1) 则表示不同位置的相同子串算作多个。

    如果无解输出 -1

    Hint

    • (1le nle 5 imes 10^5)
    • (T in {0, 1})
    • (1le kle 10^9)

    Solution

    (T = 0) 的情况比较简单,参见:「SPOJ SUBLEX」Lexicographical Substring Search

    (T = 1) 就相对麻烦得多了,接下来我们一起讨论。

    我们设 ( ext{cnt}(x)) 为结点 (x)点权。当 (T = 0) 时,( ext{cnt}(x) = 1)(T = 1) 时,( ext{cnt}(x)) 就是状态 (x) 所对应的 ( ext{end-pos}) 集合大小,因为其对应的每个串都有 (| ext{end-pos}(i)|) 个。

    ( ext{end-pos}) 集的大小怎么求呢?这个简单,直接拓扑,倒序向 ( ext{link}) 方向加就行。

    但光有 ( ext{cnt}) 还不够,我们还需引入一个 ( ext{sum})( ext{sum}(x)) 表示 经过 (x) 的子串数量

    然后就是 (k- ext{th}) 问题的套路:假如当前走到点 (x)从字典序最小的开始,以 ( exttt{a} ightarrow exttt{z}) 的顺序扫去。当扫到字符 (c) 时,设 (y = delta(x, c)),那么:

    • ( ext{sum}(y) ge k)(x) 应该走这条转移,更新答案。(kleftarrow k - ext{cnt}(y)),因为还要减去自己的权值(贡献)。结束对 (c) 的循环。
    • ( ext{otherwise})(kleftarrow k - ext{cnt}(y)),将 (c) 设成下一个字符,尝试下一个转移。

    时间复杂度(map 版):(O(nlog Sigma + | ext{ans}|)),空间复杂度:(O(n))

    Code

    /*
     * Author : _Wallace_
     * Source : https://www.cnblogs.com/-Wallace-/
     * Problem : LOJ #2102 TJOI2015 弦论
     */
    #include <iostream>
    #include <map>
    #include <string>
    
    using namespace std;
    const int N = 5e5 + 5;
    
    namespace SAM {
    	const int T = N << 1;
    	struct Node {
    		map<char, int> ch;
    		int link, len;
    		int sum, cnt;
    	} t[T];
    	int last, total;
    	
    	void extend(char c) {
    		int p = last, np = last = ++total;
    		t[np].len = t[p].len + 1;
    		
    		for (; p && !t[p].ch.count(c); p = t[p].link)
    			t[p].ch[c] = np;
    		
    		if (!p) {
    			t[np].link = 1;
    		} else {
    			int q = t[p].ch[c];
    			if (t[q].len == t[p].len + 1) {
    				t[np].link = q;
    			} else {
    				int nq = ++total;
    				t[nq].ch = t[q].ch, t[nq].link = t[q].link;
    				t[nq].len = t[p].len + 1;
    				t[np].link = t[q].link = nq;
    				for (; p && t[p].ch[c] == q; p = t[p].link)
    					t[p].ch[c] = nq;
    			}
    		}
    		t[np].cnt = 1;
    	}
    	
    	void init(string& s) {
    		total = last = 1;
    		for (string::iterator it = s.begin(); it != s.end(); it++)
    			extend(*it);
    	}
    	
    	int b[T], c[T];
    	void topo_sort() {
    		for (register int i = 1; i <= total; i++) ++c[t[i].len];
    		for (register int i = 1; i <= total; i++) c[i] += c[i - 1];
    		for (register int i = 1; i <= total; i++) b[c[t[i].len]--] = i;
    	}
    	
    	void compute(int type) {
    		for (register int i = total; i; i--) {
    			int x = b[i];
    			if (type == 0) t[x].cnt = 1;
    			else t[t[x].link].cnt += t[x].cnt;
    		}
    		
    		t[1].cnt = 0;
    		for (register int i = total; i; i--) {
    			int x = b[i];
    			t[x].sum = t[x].cnt;
    			for (map<char, int>::iterator it = t[x].ch.begin(); it != t[x].ch.end(); it++)
    				t[x].sum += t[it->second].sum;
    		}
    	}
    	
    	string get_kth(int k) {
    		int x = 1; string ret = "";
    		if (k > t[x].sum) return "-1";
    		while (k)
    			for (map<char, int>::iterator it = t[x].ch.begin(); it != t[x].ch.end(); it++) {
    				int y = it->second;
    				if (k <= t[y].sum) {
    					k -= t[y].cnt;
    					x = y, ret += it->first;
    					break;
    				} else k -= t[y].sum;
    			}
    		return ret;
    	}
    };
    
    signed main() {
    	ios::sync_with_stdio(false);
    	int t, k;
    	string str;
    	
    	cin >> str >> t >> k;
    	SAM::init(str);
    	SAM::topo_sort();
    	SAM::compute(t);
    	
    	cout << SAM::get_kth(k) << endl;
    	return 0;
    }
    
  • 相关阅读:
    [HNOI2002]营业额统计 (Splay)
    [POJ1664] 放苹果 (动态规划,组合数学)
    [AHOI2009]维护序列 (线段树)
    类型转换求和
    懒人创造方法
    编程的精义
    10-instanceof
    9-接口
    6-SUPER关键字
    5-重写与重载
  • 原文地址:https://www.cnblogs.com/-Wallace-/p/12943506.html
Copyright © 2020-2023  润新知