SPOJ SUBLEX (后缀自动机)

SPOJ SUBLEX

Problem : 给一个长度为n的字符串,要求输出字典序第k小的子串。
Solution :首先对字符串建立后缀自动机。从根节点开始根据nt数组即后缀链进行一边dfs记忆化搜索,预处理出当前状态下之后还能形成的字符串数量。对于每一个询问,从小到大枚举每一位的每个字母即可。

#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 250008;

struct Suffix_Automaton
{
	int nt[N << 1][26], a[N << 1], fail[N << 1];
	int tot, last, root;
	int p, q, np, nq;
	int cnt[N << 1];
	int newnode(int len)
	{
		for (int i = 0; i < 26; ++i) nt[tot][i] = -1;
		fail[tot] = -1; a[tot] = len; cnt[tot] = 0;
		return tot++;
	}
	void clear()
	{
		tot = last = 0; 
		root = newnode(0);
	}
	void insert(int ch, int k)
	{
		p = last; last = np = newnode(a[p] + 1); 
		for (; ~p && nt[p][ch] == -1; p = fail[p]) nt[p][ch] = np;
		if (p == -1) fail[np] = root;
		else
		{
			q = nt[p][ch];
			if (a[p] + 1 == a[q]) fail[np] = q;
			else
			{
				nq = newnode(a[p] + 1);
				for (int i = 0; i < 26; ++i) nt[nq][i] = nt[q][i];
				fail[nq] = fail[q];
				fail[np] = fail[q] = nq;
				for (; ~p && nt[p][ch] == q; p = fail[p]) nt[p][ch] = nq;
			}
		}
	}
	int dfs(int u)
	{
		if (cnt[u]) return cnt[u];
		cnt[u] = 1;
		for (int i = 0; i < 26; ++i)
			if (~nt[u][i])
			{
				dfs(nt[u][i]);
				cnt[u] += cnt[nt[u][i]];
			}
	}
	void solve(int k)
	{
		for (int p = root; k > 0;)
		{
			for (int i = 0; i < 26; ++i)
				if (~nt[p][i])	
					if (k <= cnt[nt[p][i]])
					{
						printf("%c",'a' + i);
						k--;
						p = nt[p][i];
						break;
					}
					else k -= cnt[nt[p][i]];
		}
		printf("\n");
	}
}sam;

int main()
{
	cin.sync_with_stdio(0);
	sam.clear();
	string s; cin >> s;
	for (int i = 0, len = s.length(); i < len; ++i)
		sam.insert(s[i] - 'a', i);
	sam.dfs(sam.root);
	int q; cin >> q;
	for (int i = 1; i <= q; ++i)
	{
		int k; cin >> k;
		sam.solve(k);
	}
}


posted @ 2017-07-21 17:01  rpSebastian  阅读(272)  评论(0编辑  收藏  举报