zoukankan      html  css  js  c++  java
  • SPOJ BEADS Glass Beads 最小循环串

    vjudge传送门


    题面:(用SAM)求串的最小表示法。


    首先最小表示法有(O(n))的做法,但老师把这道题放这是让我们练SAM的,那就说SAM的做法吧。
    首先把串的二倍都扔到SAM中,然后可以类比暴力的做法:
    暴力的做法是从多个字典序最小的字符开始一位一位的比较,直到当前字典序最小的字符只出现在一个位置,那么这个位置就是最小表示法所在的位置。
    那么在SAM上,因为多个相同字母的位置都在一个节点,所以我们直接从0点出发,每次走字典序最小的出边,一直走到这个点的endpos个数是1为止。那么这个位置减去匹配长度就是最小表示法的起点了。
    值得一提的是,endpos个数为1的点必定是后缀链接树的叶子节点,因为克隆出来的节点至少连着两个(非克隆)儿子,所以endpos个数至少为2.

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<queue>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const int maxn = 2e4 + 5;
    const int maxs = 27;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    char s[maxn];
    
    struct Sam
    {
    	int tra[maxn << 1][maxs], link[maxn << 1], len[maxn << 1], siz[maxn << 1], edp[maxn << 1], cnt, las;
    	In void init() 
    	{
    		link[cnt = las = 0] = -1;
    		Mem(tra[0], 0), Mem(siz, 0), Mem(edp, 0);
    		Mem(buc, 0), Mem(pos, 0);
    	}	
    	In void insert(int c, int id)
    	{
    		int now = ++cnt, p = las; Mem(tra[now], 0);
    		len[now] = len[p] + 1; 
    		siz[now] = 1, edp[now] = id;
    		while(~p && !tra[p][c]) tra[p][c] = now, p = link[p];
    		if(p == -1) link[now] = 0;
    		else
    		{
    			int q = tra[p][c];
    			if(len[q] == len[p] + 1) link[now] = q;
    			else
    			{
    				int clo = ++cnt;
    				memcpy(tra[clo], tra[q], sizeof(tra[q]));
    				len[clo] = len[p] + 1;
    				link[clo] = link[q]; link[q] = link[now] = clo;
    				while(~p && tra[p][c] == q) tra[p][c] = clo, p = link[p];
    			}
    		}
    		las = now;
    	}
    	int buc[maxn << 1], pos[maxn << 1];
    	In int solve()
    	{
    		for(int i = 1; i <= cnt; ++i) buc[len[i]]++;
    		for(int i = 1; i <= cnt; ++i) buc[i] += buc[i - 1];
    		for(int i = 1; i <= cnt; ++i) pos[buc[len[i]]--] = i;
    		for(int i = cnt; i; --i) siz[link[pos[i]]] += siz[pos[i]];
    		for(int p = 0, l = 0;;) 
    		{
    			int c = 0; while(c < maxs && !tra[p][c]) ++c;
    			p = tra[p][c], ++l;		//一直沿最小的出边走 
    			if(siz[p] == 1) return edp[p] + 1 - l + 1;
    		}
    		return -1;
    	}
    }S;
    
    int main()
    {
    	int T = read();
    	while(T--)
    	{
    		scanf("%s", s);
    		int n = strlen(s);
    		S.init();
    		for(int i = 0; i < n; ++i) S.insert(s[i] - 'a', i);
    		for(int i = 0; i < n; ++i) S.insert(s[i] - 'a', i + n);
    		write(S.solve()), enter;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Docker 部署 Nginx
    Docker 安装 Redis
    linux shell "2>&1"
    定时备份docker mysql
    SpringBoot 中拦截器和过滤器的使用
    SpringBoot WebMvcConfigurer
    springboot自定义参数解析HandlerMethodArgumentResolver
    mysql在linux下查看my.cnf位置的方法
    Linux下设置mysql允许远程连接
    Android项目实战(六十):修改项目包名
  • 原文地址:https://www.cnblogs.com/mrclr/p/14556738.html
Copyright © 2011-2022 走看看