这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法.....

求出Height数组后, 我们枚举每一位当做子串的开头.

如上图(x, y是height值), Heights数组中相邻的3个后缀, 假如我们枚举s2的第一个字符为开头, 那我们发现, 长度至少为len = max(x, y)+1, 才能满足题意(仅出现一次). 这个很好脑补...因为s2和其他串的LCP是RMQ, 肯定会<=LCP(s1,s2)或<=LCP(s2,s3). 然后就用len去更新s2中前len个字符的答案, 线段树维护. 然后对于长度lth>len的也肯定是合法的, 他们对s2的前lth个字符都有贡献...但是事实上lth对前lth-1个字符c的贡献是没有卵用的....(因为小于同样字符开头的以c结尾的串的贡献或者是len的贡献), 所以lth>=len对第lth个字符有贡献.

容易看出这样的贡献是成等差数列的。。。。线段树维护就OK了.

时间复杂度O(N log N), 空间复杂度O(N)

-------------------------------------------------------------------------

#include<cstdio>
#include<cstring>
#include<algorithm>
 
using namespace std;
 
const int maxn = 100009;
 
char S[maxn];
int N, L, R, Val;
int Height[maxn], Rank[maxn], Sa[maxn], cnt[maxn];
 
inline void Min(int &x, int t) {
if(t < x) x = t;
}
inline void Max(int &x, int t) {
if(t > x) x = t;
}
 
void BuildSA(int m) {
int *x = Height, *y = Rank;
for(int i = 0; i < m; i++) cnt[i] = 0;
for(int i = 0; i < N; i++) cnt[x[i] = S[i]]++;
for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1];
for(int i = N; i--; ) Sa[--cnt[x[i]]] = i;
for(int k = 1, p = 0; k <= N; k <<= 1, p = 0) {
for(int i = N - k; i < N; i++) y[p++] = i;
for(int i = 0; i < N; i++)
if(Sa[i] >= k) y[p++] = Sa[i] - k;
for(int i = 0; i < m; i++) cnt[i] = 0;
for(int i = 0; i < N; i++) cnt[x[y[i]]]++;
for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1];
for(int i = N; i--; ) Sa[--cnt[x[y[i]]]] = y[i];
swap(x, y);
p = (x[Sa[0]] = 0) + 1;
for(int i = 1; i < N; i++) {
if(y[Sa[i]] != y[Sa[i - 1]] || y[Sa[i] + k] != y[Sa[i - 1] + k]) p++;
x[Sa[i]] = p - 1;
}
if((m = p) >= N) break;
}
for(int i = 0; i < N; i++) Rank[Sa[i]] = i;
Height[0] = 0;
for(int i = 0, h = 0; i < N; i++) if(Rank[i]) {
if(h) h--;
while(S[i + h] == S[Sa[Rank[i] - 1] + h]) h++;
Height[Rank[i]] = h;
}
}
 
struct Node {
Node *lc, *rc;
int n, d;
inline void pd(int len) {
if(n != maxn) {
Min(lc->n, n);
Min(rc->n, n);
}
if(d != maxn) {
Min(lc->d, d);
Min(rc->d, d + ((len + 1) >> 1));
}
}
} pool[maxn << 1], *pt = pool, *Root;
 
void Build(Node* t, int l, int r) {
t->n = t->d = maxn;
if(l != r) {
int m = (l + r) >> 1;
Build(t->lc = pt++, l, m);
Build(t->rc = pt++, m + 1, r);
}
}
 
void Modify(Node* t, int l, int r) {
if(L <= l && r <= R) {
Min(t->n, Val);
} else {
int m = (l + r) >> 1;
if(L <= m) Modify(t->lc, l, m);
if(m < R) Modify(t->rc, m + 1, r);
}
}
 
void Change(Node* t, int l, int r) {
if(L <= l && r <= R) {
Min(t->d, Val + l - L);
} else {
int m = (l + r) >> 1;
if(L <= m) Change(t->lc, l, m);
if(m < R) Change(t->rc, m + 1, r);
}
}
 
void DFS(Node* t, int l, int r) {
if(l != r) {
int m = (l + r) >> 1;
t->pd(r - l + 1);
DFS(t->lc, l, m);
DFS(t->rc, m + 1, r);
} else 
printf("%d\n", min(t->d, t->n));
}
 
int main() {
scanf("%s", S);
N = strlen(S);
S[N++] = '$';
BuildSA('z' + 1);
int n = N - 1;
Build(Root = pt++, 1, n);
Height[N] = 0;
for(int i = 1; i < N; i++) {
Val = max(Height[i], Height[i + 1]) + 1;
if(Val > 1) {
if(Sa[i] + Val > n) continue;
L = Sa[i] + 1, R = L + Val - 2;
Modify(Root, 1, n);
}
L = Sa[i] + Val, R = n;
if(L > R) continue;
Change(Root, 1, n);
}
DFS(Root, 1, n);
return 0;
}

-------------------------------------------------------------------------

1396: 识别子串

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 201  Solved: 119
[Submit][Status][Discuss]

Description

Input

一行,一个由小写字母组成的字符串S,长度不超过10^5

Output

L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长.

Sample Input

agoodcookcooksgoodfood

Sample Output

1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4

HINT

Source

BZOJ 1396: 识别子串( 后缀数组 + 线段树 )的更多相关文章

  1. BZOJ 1396 识别子串 (后缀自动机+线段树)

    题目大意: 给你一个字符串S,求关于每个位置x的识别串T的最短长度,T必须满足覆盖x,且T在S中仅出现一次 神题 以节点x为结尾的识别串,必须满足它在$parent$树的子树中只有一个$endpos$ ...

  2. bzoj 1396: 识别子串【SAM+线段树】

    建个SAM,符合要求的串显然是|right|==1的节点多代表的串,设si[i]为right集合大小,p[i]为right最大的r点,这些都可以建出SAM后再parent树上求得 然后对弈si[i]= ...

  3. BZOJ1396: 识别子串(后缀自动机 线段树)

    题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...

  4. bzoj 1396: 识别子串 && bzoj 2865: 字符串识别【后缀数组+线段树】

    根据height数组的定义,和当前后缀串i最长的相同串的长度就是max(height[i],height[i+1]),这个后缀贡献的最短不同串长度就是len=max(height[i],height[ ...

  5. BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)

    题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...

  6. bzoj 1396 识别子串 后缀树+线段树

    题目大意 给定一个长度\(\le100000\)的字符串 求每一个位置的最短识别子串 对于位置\(x\),能识别子串\(s[i...j]\)的条件是 1.\(i\le x \le j\) 2.\(s[ ...

  7. BZOJ 1396&&2865 识别子串[后缀自动机 线段树]

    Description 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: 1.i≤K≤j. 2.子串T只在S中出现过一次. 例如, ...

  8. BZOJ 1396 识别子串 (后缀自动机、线段树)

    手动博客搬家: 本文发表于20181221 00:58:26, 原地址https://blog.csdn.net/suncongbo/article/details/85150962 嗯,以后博客内容 ...

  9. BZOJ 2865 字符串识别(后缀数组+线段树)

    很容易想到只考虑后缀长度必须为\(max(height[rk[i]],height[rk[i]+1])+1\)(即\([i,i+x-1]\)代表的串只出现过一次)然后我正着做一遍反着做一遍,再取一个\ ...

随机推荐

  1. 【main()的参数探究】

    恩...今天研究信安的课件的时候看到一段对于main(int argc,char *argv[])的编程 所以探究探究main()函数的参数 探究程序如下: #include <cstdio&g ...

  2. UVA 10603 Fill

    题意: 题目的意思是倒水,给出的四个数据是第一个水杯,第二个水杯,第三个水杯,和目标水量.一开始只有第三个水杯是满的,剩下的水杯是空的.倒水的时候只能把倒水出来的这个杯子倒空,或是倒水进去的杯子倒满. ...

  3. Asp.Net实现Http长连接推送

    话说最新帮一个朋友搞智能家居方面的东西,做一个云平台.主要作用手机在局域网外环境时对手机客户端和智能网关中命令的互相转发. 目前已经有了一个稳定的Socket版本,但是考虑到以后的扩展和性能指标要改成 ...

  4. q.js实现nodejs顺序调用

    nodejs的异步调用有时候是最让人头疼的,如何能是一些代码顺序的执行呢,这里和大家分享nodejs的promise 什么是promise promise一个标准,它描述了异步调用的返回结果,包括正确 ...

  5. Android_使用getIdentifier()获取资源Id

    Android 获取资源ID的另外一种方法,常规获取ID是在特定的文件夹下面的资源,如果在比较特殊的文件夹下面,就需要其他方法获取ID 了: 使用getIdentifier()方法可以方便的获各应用包 ...

  6. 关于 <video> 的兼容性测试

    测试浏览器 Firefox: 33.0.1 Chrome: 38.0.2125.111 m Safari: 5.1.7 IE: 9 和 10(6,7,8不考虑) 经测试:FF.Chrome.Safar ...

  7. JavaScript基本概念(对象)

    1.对象的分类 内置对象:由ECMAScript规范定义的对象或类 宿主对线:由浏览器定义的对象 自定义对象:由运行中的Javascript代码创建的对象 2.属性的分类 自有属性:直接在对象中定义的 ...

  8. PHP中将对数据库的操作,封装成一个工具类以及学会使用面向对象的方式进行编程

    <?php class SqlTool { //属性 private $conn; private $host="localhost"; private $user=&quo ...

  9. text-overflow: ellipsis;省略号颜色设置

    参考stackoverfolow 想要给元素添加text-overflow: ellipsis;样式,必须保证改其父元素是块级元素,如果其是行级元素,可以通过设置display:block;或者dis ...

  10. Python学习笔记(一)基础

    学习资料 跟着廖雪峰的Python教程学习Python,大家可以去官方网站查看学习教程.使用的Python版本为3.0.x,解释器为CPython.本系列博客为学习笔记,记录跟随廖老师所学知识,同时会 ...