标签:最大值 isp 长度 nbsp ted 而且 code eof open
终于遇到了一道后缀数组不能过 一定要学SAM的题。。。
(看了半个下午+半个上午)
现在总结一下(是给我自己总结。。所以只总结了我觉得重要的 。。
看不太懂的话可以To http://blog.csdn.net/clover_hxy/article/details/53758535 图文并茂
或者 去看更长更详细的陈立杰PPT http://wenku.baidu.com/link?url=9YEHHchtr0vyGGDZAcsMYPI3l_Q82UNPuS4KqkfrlG_t5NFk_9iXZd86Uq4uDqqLUKFJL7ZINkxQbstwqaF6OEFes3elFsXmbDZVIsgVwti:
SAM Suffix AutoMaton [AutoMaton的定义 不懂自己查吧 ]
首先 (N为 字符串长度*2) 最基本的SAM 要开的数组 (其含义与用处在之后说明):pr[N] (别人的代码版本还有fa、pre、link...变量名而已 别在意), ch[N][26(当然不一定是26个字母)],
ma[N](意思是maxlen,别人的有step、len...),还要cnt和last两个全局变量,p,np,q,nq 四个局部变量。 root就设为1,所以不需要开个变量rt。
SAM为什么能用O(n)的空间存下n^2的子串? 我是这样理解的{
每个节点 对应的是 一个独一无二的right集合
right集合是什么?【比如说 一个S字符串为 AABABA ,子串BA有2个 对吧 .所有子串BA 对应的右端点集合(即right集合)为{4,6},子串ABA也有2个 ,right集合也是{4,6}】
那么 BA和ABA的right集合一样 BA和ABA 被自动机从rt状态 一步一步转移 最终到达同一个节点
设这个节点 编号为v,v不仅对应了 {4,6}这个right集合 同时还对应了 子串BA和ABA 可以看出 v代表的子串 其右端点是 同时属于 集合{4,6}的 【什么叫同时属于? 看看AABA,它的右端点只能是4,不能是6】,还可以看出 只有长度为2~3的子串可以 满足“同时属于” ,【如长度为1的A, 它不仅属于{4,6} 它属于{1,2,4,6}; 长度为4的AABA,它只属于{4}——————所以它们属于另外的节点】
这里长度为2~3可能不是很明显 但其实可行的长度 一定是连续的min~max 想想就明白了。。
[v节点对应 的子串 长度为min(v)~max(v)] 这个信息由 ma[]存储,ma[v]=max(v) 但它只存储了最大值。 最小值呢? [这里先提一下 ,min(v) 是 ma[pr[v]]+1,接下来会说。。
那么我们来证明节点数是 O(N)的 (准确说是 小于 length(S)*2)
先来看 上面那个字符串AABABA , 对它建出的SAM有 个节点,通过pr[] 构成一棵树,最好在草稿纸上画个图
以下一一说明:
1号 即root节点(我简写成rt) 对应right集合{1,2,3,4,5,6} 长度是 0~0 pr[1]=0
↙以下x号只是我给节点标个号,可能真正的数组下标不是这样的,不要在意,但1号一定是1。
2号 {1,2,4,6} 长度是 1~1 对应子串:A,A,A,A pr[2]=1
3号 {3,5} 长度1~2 pr[3]=1 对应:B,B,AB,AB
4号 {2} 长度2~2 pr[4]=2 对应:AA
5号 {4,6} 长度2~3 pr[5]=2 对应:BA,BA,ABA,ABA
6号 {3} 长度3~3 pr[6]=3 对应:AAB
7号 {5} 长度3~5 pr[7]=3 对应:BAB,ABAB,AABAB
8号 {4} 长度4~4 pr[8]=5 对应:AABA
9号 {6} 长度4~6 pr[9]=5 对应:BABA,ABABA,AABABA
从根到叶子 就是对集合{1,2,...,n}不断地分割成至少两个部分
可以看出来
*叶子节点最多n个 而且每个非叶子节点都有分叉导致right集合不断变小 所以显然空间是O(N)的
*再数一下 对应的子串 总共是 6*(6+1)/2=21个,它们包含了S的所有子串
*对于一个节点v,它的right集合有k个元素,长度范围是min(v)~max(v)
*观察 min(v)=ma[pr[v]]+1 这条性质,好好理解一下 长度越小的串在S中出现次数越多 所以right集合越大,
所以 从根到叶子 的right集合不断变小,而min和max在增加。。 这样就明白pr[]是什么了吧。
}
那么现在讲 ch[v][c],挺好理解 ,就是 从v节点 再读入一个c字符(字符已经转成int了)之后会转移到 的节点
所有节点有ch[v][c]构成一个拓扑图 注意 这个拓扑图的结构 与pr[]连成的树 毫无关联。 只是两者之间满足了一些性质(之后讲)
先上一下代码 对字符串S建出SAM (是一个字符一个字符添加的)
1 void add(int c){ 2 int p,np,q,nq; 3 p=last; last=np=++cnt; ma[np]=ma[p]+1; 4 while (!ch[p][c]&&p) ch[p][c]=np,p=pr[p]; 5 if (!p) pr[np]=1; else{ 6 q=ch[p][c]; 7 if (ma[p]+1==ma[q]) pr[np]=q; 8 else{ 9 nq=++cnt; ma[nq]=ma[p]+1; 10 pr[nq]=pr[q]; pr[q]=pr[np]=nq; 11 memcpy(ch[nq],ch[q],sizeof ch[q]); 12 while (ch[p][c]==q) ch[p][c]=nq,p=pr[p]; 13 } 14 } 15 }
不知道前面说的清不清楚 但是看到这段代码 大多数人第一反应 应该是一脸懵逼的。。
现在我来(对着一些语句)解答疑问(如果你疑问很多 不要像我一样死钻一个问题。 你可以先看完,然后说不定就能解决前面的疑问):
& cnt,last是全局变量,cnt就是计数,不多说。。last(之前忘记提了)是上一次add操作后,加入的那个节点(注意不一定是cnt-1);
&
...今天先写到这里、、、好累。。。
标签:最大值 isp 长度 nbsp ted 而且 code eof open
原文地址:http://www.cnblogs.com/cyz666/p/6527196.html