zoukankan      html  css  js  c++  java
  • P4051 [JSOI2007]字符加密 SAM

    我承认我是个傻逼

    也只有我这么离谱会用(SAM)来求(SA)

    对这道题来说,还是比较简单的,只需要复制原串一遍贴到后面,然后求一遍(SA)即可


    接下来我给诸位介绍一下我研究了两个下午,然后(MLE)的成果


    首先我们对字符串建出(SAM),考虑S(AM)的本质是一个(DAG)图和一棵后缀树

    因为我们要对后缀进行排序,所以我们要尽可能的利用后缀树来解决问题

    很明显在后缀树上按字典序遍历即可得到(SA)

    所以我们现在的问题就是如何解决如何按字典序遍历

    求出其父节点与自身加长的串中第一个字符大小,用它来排序

    然后加边(dfs)即可

    算了我来给你们翻译一下(solve)函数

    inline void solve()
    {
    	for(int i=1;i<=cnt;i++) link[i] = ge(c[n + 1 - Right[i] + node[node[i].fa].len]) , bac[link[i]]++;//求出第一个字符
    	for(int i=1;i<=256;i++) bac[i]+= bac[i-1];
    	for(int i=1;i<=cnt;i++) Ti[bac[link[i]]--] = i;//相当于把所有link都丢进64个桶里,节点越大在对应的桶里越靠后
    	for(int i=cnt;i>=1;i--) add(node[Ti[i]].fa , Ti[i]);//倒着加边意味着z类边优先加入
    }
    

    (Code)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define INF 1ll<<30
    #define ill long long
    #define sto set<node>::iterator
    
    template<typename _T>
    inline void read(_T &x)
    {
    	x=0;char s=getchar();int f=1;
    	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
    	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
    	x*=f;
    }
    
    int ge(char ch)
    {
        if (ch<='9' && ch>='0') return ch-'0';
        if (ch<='Z' && ch>='A') return ch-'A'+10;
        if (ch<='z' && ch>='a') return ch-'a'+36;
        return ch;
    }
    const int np = 2e5+5;
    
    struct SAM{
    	int fa;
    	int len;
    	int son[80];
    	int pos;
    }node[np * 2];
    
    int cnt = 1 , la = 1,n;
    int Right[np * 2];
    inline void insert(int x,int i)
    {
    	int  k,p,q,now;
    	p = la,now = la = ++cnt;
    	Right[now] =node[now].len = node[p].len + 1, node[now].pos = i;
    	for( ;p && !node[p].son[x] ;node[p].son[x] = now , p = node[p].fa );
    	if(!p) return (void)(node[now].fa = 1);
    	if(node[p].len + 1 == node[q = node[p].son[x] ].len) return (void)(node[now].fa = q);
    	node[k = ++cnt].len = node[p].len + 1 , node[k].fa = node[q].fa ; node[q].fa = node[now].fa = k,Right[k] = Right[q] ,memcpy(node[k].son , node[q].son , sizeof(node[q].son));
    	for( ; p&& !(node[p].son[x] ^ q) ; node[p].son[x] = k , p = node[p].fa) ;
    }
    
    int link[np * 2] , bac[np * 2];
    int Ti[np * 2],head[np * 2] , nxt[np * 2] , ver[np *2];
    int tit;
    char c[np * 2];
    
    inline void add(int a,int b)
    {
    	ver[++tit] = b;
    	nxt[tit] = head[a];
    	head[a] = tit;
    }
    
    inline void solve()
    {
    	for(int i=1;i<=cnt;i++) link[i] = ge(c[n + 1 - Right[i] + node[node[i].fa].len]) , bac[link[i]]++;
    	for(int i=1;i<=256;i++) bac[i]+= bac[i-1];
    	for(int i=1;i<=cnt;i++) Ti[bac[link[i]]--] = i;
    	for(int i=cnt;i>=1;i--) add(node[Ti[i]].fa , Ti[i]);
    }
    
    int sa[np * 2];
    int gs = 0;
    
    inline void dfs(int x)
    {
    	if(node[x].pos)	sa[++gs] = node[x].pos;
    	for(int i=head[x];i;i=nxt[i])
    	dfs(ver[i]);
    }
    
    signed  main()
    {
    	scanf("%s",c+1);
    	n = strlen(c+1);
    	int Len  = n;
    	for(int i=1;i<=n;i++)
    	c[i + n] = c[i];
    	n = strlen(c + 1);
    	for(int i=n;i>=1;i--) insert(ge(c[i]) , i);
    	solve();
    	dfs(1);
    	for(int i=1;i<=gs;i++)
    	{
    		if(sa[i] > Len) continue;
    		else
    		{
    			cout<<c[sa[i] + Len - 1];
    		}
    	}
     }
    

    (End)

    事实证明(SAM)的最大阻力就是空间太小

    (10^6)以内(128MB)是解决不了问题的
    (map)也没用

  • 相关阅读:
    BZOJ3498PA2009 Cakes——三元环
    黑科技之三元环讲解
    BZOJ4317Atm的树&BZOJ2051A Problem For Fun&BZOJ2117[2010国家集训队]Crash的旅游计划——二分答案+动态点分治(点分树套线段树/点分树+vector)
    BZOJ2463[中山市选2009]谁能赢呢?——博弈论
    BZOJ2275[Coci2010]HRPA——斐波那契博弈
    BZOJ2281[Sdoi2011]黑白棋&BZOJ4550小奇的博弈——DP+nimk游戏
    BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)
    Trie树学习总结
    kmp学习小结
    Hash学习小结
  • 原文地址:https://www.cnblogs.com/-Iris-/p/15350304.html
Copyright © 2011-2022 走看看