[POJ1226]Substrings(后缀数组)

传送门

给定 n 个字符串,求出现或反转后出现在每个字符串中的最长子串。

算法分析:

这题不同的地方在于要判断是否在反转后的字符串中出现。其实这并没有加大题目的难度。

只需要先将每个字符串都反过来写一遍,中间用一个互不相同的且没有出现在字符串中的字符隔开,

再将 n 个字符串全部连起来,中间也是用一个互不相同的且没有出现在字符串中的字符隔开,求后缀数组。

然后二分答案,再将后缀分组。

判断的时候,要看是否有一组后缀在每个原来的字符串或反转后的字符串中出现。

这个做法的时间复杂度为 O(nlogn)。

——代码

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #define N 21001
  5
  6 int len, n, m, max_num, T;
  7 int buc[N], x[N], y[N], sa[N], rank[N], height[N], belong[N], s[N];
  8 char a[N];
  9 bool f[101];
 10
 11 inline void build_sa()
 12 {
 13     int i, k, p;
 14     for(i = 0; i < m; i++) buc[i] = 0;
 15     for(i = 0; i < len; i++) buc[x[i] = s[i]]++;
 16     for(i = 1; i < m; i++) buc[i] += buc[i - 1];
 17     for(i = len - 1; i >= 0; i--) sa[--buc[x[i]]] = i;
 18     for(k = 1; k <= len; k <<= 1)
 19     {
 20         p = 0;
 21         for(i = len - 1; i >= len - k; i--) y[p++] = i;
 22         for(i = 0; i < len; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
 23         for(i = 0; i < m; i++) buc[i] = 0;
 24         for(i = 0; i < len; i++) buc[x[y[i]]]++;
 25         for(i = 1; i < m; i++) buc[i] += buc[i - 1];
 26         for(i = len - 1; i >= 0; i--) sa[--buc[x[y[i]]]] = y[i];
 27         std::swap(x, y);
 28         p = 1, x[sa[0]] = 0;
 29         for(i = 1; i < len; i++)
 30             x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
 31         if(p >= len) break;
 32         m = p;
 33     }
 34 }
 35
 36 inline void build_height()
 37 {
 38     int i, j, k = 0;
 39     for(i = 0; i < len; i++) rank[sa[i]] = i;
 40     for(i = 0; i < len; i++)
 41     {
 42         if(!rank[i]) continue;
 43         if(k) k--;
 44         j = sa[rank[i] - 1];
 45         while(s[i + k] == s[j + k] && i + k < len && j + k < len) k++;
 46         height[rank[i]] = k;
 47     }
 48 }
 49
 50 inline bool check(int k)
 51 {
 52     int i, cnt = 1;
 53     memset(f, 0, sizeof(f));
 54     f[belong[sa[0]]] = 1;
 55     for(i = 1; i < len; i++)
 56         if(height[i] >= k && !f[belong[sa[i]]])
 57         {
 58             cnt++;
 59             f[belong[sa[i]]] = 1;
 60             if(cnt == n) return 1;
 61         }
 62         else if(height[i] < k)
 63         {
 64             cnt = 1;
 65             memset(f, 0, sizeof(f));
 66             f[belong[sa[i]]] = 1;
 67         }
 68     return 0;
 69 }
 70
 71 inline int solve()
 72 {
 73     int l = 1, r = len, ans = 0, mid;
 74     while(l <= r)
 75     {
 76         mid = (l + r) >> 1;
 77         if(check(mid)) ans = mid, l = mid + 1;
 78         else r = mid - 1;
 79     }
 80     return ans;
 81 }
 82
 83 int main()
 84 {
 85     int i, j, l;
 86     scanf("%d", &T);
 87     while(T--)
 88     {
 89         len = 0;
 90         m = 400;
 91         scanf("%d", &n);
 92         for(i = 0; i < n; i++)
 93         {
 94             scanf("%s", a);
 95             l = strlen(a);
 96             for(j = 0; j < l; j++) belong[len] = i, s[len++] = a[j];
 97             belong[len] = i;
 98             s[len++] = 130 + (i << 1);
 99             for(j = l - 1; j >= 0; j--) belong[len] = i, s[len++] = a[j];
100             belong[len] = i;
101             s[len++] = 130 + (i << 1 | 1);
102         }
103         len--;
104         build_sa();
105         build_height();
106         if(n == 1)
107         {
108             printf("%d\n", l);
109             continue;
110         }
111         printf("%d\n", solve());
112     }
113     return 0;
114 }

时间: 2024-10-15 20:58:16

[POJ1226]Substrings(后缀数组)的相关文章

POJ1226:Substrings(后缀数组)

Description You are given a number of case-sensitive strings of alphabetic characters, find the largest string X, such that either X, or its inverse can be found as a substring of any of the given strings. Input The first line of the input contains a

POJ 1226 Substrings (后缀数组)

题目大意: 问的是m个字符串里,都出现过的子串.子串也可以出现在这个串的逆序串中. 思路分析: 居然wa在全5个 "a" 的数据上. 二分的时候下界不能为0.. 思路大致上是把原串和逆序串全部处理出来,放入str中,然后在每个串中间加一个没有出现过的. 此处注意输入不仅仅是字母. 然后跑一遍后缀数组. 然后用标记计数就好了. #include <iostream> #include <cstdio> #include <algorithm> #inc

uva 10829 - L-Gap Substrings(后缀数组)

题目链接:uva 10829 - L-Gap Substrings 题目大意:给定一个字符串,问有多少字符串满足UVU的形式,要求U非空,V的长度为g. 解题思路:对字符串的正序和逆序构建后缀数组,然后枚举U的长度l,每次以长度l分区间,在l和l+d+g所在的两个区间上确定U的最大长度. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using nam

poj Common Substrings(后缀数组&amp;单调队列)

Common Substrings Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 7082   Accepted: 2355 Description A substring of a string T is defined as: T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|. Given two strings A, B and one integer K, we define S, a

POJ 3415 Common Substrings 后缀数组+并查集

后缀数组,看到网上很多题解都是单调栈,这里提供一个不是单调栈的做法, 首先将两个串 连接起来求height   求完之后按height值从大往小合并.  height值代表的是  sa[i]和sa[i-1] 的公共前缀长度,那么每次合并就是合并  i和i-1 那么在合并小的时候公共前缀更大的肯定已经都合并在一起,那么就可以直接统计了. #include<iostream> #include<cstdio> #include<algorithm> #include<

spoj Distinct Substrings 后缀数组

给定一个字符串,求不相同的子串的个数. 假如给字符串“ABA";排列的子串可能: A B A AB  BA ABA 共3*(3+1)/2=6种; 后缀数组表示时: A ABA BA 对于A和AB height[i]=1; 表明一个长度公共,所以ABA中多出现了A这个子串,所以6-1=5: 对于ABA BA height[i]=0,所以不需要减去. 最后答案为5: #include<iostream> #include<stdio.h> #include<string

SPOJ 694 || 705 Distinct Substrings ( 后缀数组 &amp;&amp; 不同子串的个数 )

题意 : 对于给出的串,输出其不同长度的子串的种类数 分析 : 有一个事实就是每一个子串必定是某一个后缀的前缀,换句话说就是每一个后缀的的每一个前缀都代表着一个子串,那么如何在这么多子串or后缀的前缀中找出不同的并计数呢?思路就是所有的可能子串数 - 重复的子串数.首先我们容易得到一个长度为 len 的串的子串数为 len * ( len + 1) / 2.那如何知道重复的子串数呢?答案就是利用后缀数组去跑一遍 Height ,得到所有的最长公共前缀(LCP),这些最长公共前缀的值都存在了 He

POJ 3415 Common Substrings(后缀数组求重复字串)

题目大意:给你两个字符串,让你求出来两个字符串之间的重复子串长度大于k的有多少个. 解题思路: 先说论文上给的解释:基本思路是计算A的所有后缀和B的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于k的部分全部加起来.先将两个字符串连起来,中间用一个没有出现过的字符隔开.按height值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和.扫描一遍,每遇到一个B的后缀就统计与前面的A的后缀能产生多少个长度不小于k的公共子串,这里A的后缀需要用一个单调的栈来高效的维护.然后对

SPOJ694&amp;&amp;SPOJ705:Distinct Substrings(后缀数组)

Description Given a string, we need to find the total number of its distinct substrings. Input T- number of test cases. T<=20; Each test case consists of one string, whose length is <= 1000 Output For each test case output one number saying the numb

SPOJ694--- DISUBSTR - Distinct Substrings(后缀数组)

Given a string, we need to find the total number of its distinct substrings. Input T- number of test cases. T<=20; Each test case consists of one string, whose length is <= 1000 Output For each test case output one number saying the number of distin