【poj1226-出现或反转后出现在每个串的最长公共子串】后缀数组

题意:求n个串的最长公共子串,子串出现在一个串中可以是它的反转串出现。总长<=10^4.

题解:

对于每个串,把反转串也连进去。
二分长度,分组,判断每个组。

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 using namespace std;
  6
  7 const int N=2*21000;
  8 int n,sl,cl,c[N],rk[N],sa[N],Rs[N],wr[N],y[N],h[N],st[N],ed[N],fst[N],fed[N];
  9 char s[N];
 10 bool vis[N];
 11
 12 void get_sa(int m)
 13 {
 14     for(int i=1;i<=cl;i++) rk[i]=c[i];
 15     for(int i=1;i<=m;i++) Rs[i]=0;
 16     for(int i=1;i<=cl;i++) Rs[rk[i]]++;
 17     for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1];
 18     for(int i=cl;i>=1;i--) sa[Rs[rk[i]]--]=i;
 19
 20     int ln=1,p=0;
 21     while(p<cl)
 22     {
 23         int k=0;
 24         for(int i=cl-ln+1;i<=cl;i++) y[++k]=i;
 25         for(int i=1;i<=cl;i++) if(sa[i]>ln) y[++k]=sa[i]-ln;
 26
 27         for(int i=1;i<=cl;i++) wr[i]=rk[y[i]];
 28         for(int i=1;i<=m;i++) Rs[i]=0;
 29         for(int i=1;i<=cl;i++) Rs[wr[i]]++;
 30         for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1];
 31         for(int i=cl;i>=1;i--) sa[Rs[wr[i]]--]=y[i];
 32
 33         for(int i=1;i<=cl;i++) wr[i]=rk[i];
 34         for(int i=cl+1;i<=cl+ln;i++) wr[i]=0;
 35         p=1;rk[sa[1]]=1;
 36         for(int i=2;i<=cl;i++)
 37         {
 38             if(wr[sa[i]]!=wr[sa[i-1]] || wr[sa[i]+ln]!=wr[sa[i-1]+ln]) p++;
 39             rk[sa[i]]=p;
 40         }
 41         ln*=2,m=p;
 42     }
 43     sa[0]=0,rk[0]=0;
 44 }
 45
 46 void get_h()
 47 {
 48     int k=0,j;
 49     for(int i=1;i<=cl;i++) if(rk[i]!=1)
 50     {
 51         j=sa[rk[i]-1];
 52         if(k) k--;
 53         while(c[i+k]==c[j+k] && i+k<=cl && j+k<=cl) k++;
 54         h[rk[i]]=k;
 55     }
 56     h[0]=0;
 57 }
 58
 59 int idx(int x)
 60 {
 61     for(int i=1;i<=n;i++)
 62         if((st[i]<=x && x<=ed[i]) || (fst[i]<=x && x<=fed[i]))  return i;
 63 }
 64
 65 bool check(int k)
 66 {
 67     memset(vis,0,sizeof(vis));
 68     for(int i=1;i<=cl;i++)
 69     {
 70         if(h[i]<k)
 71         {
 72             int cnt=0;
 73             for(int j=1;j<=n;j++) if(vis[j]) cnt++;
 74             if(cnt==n) return 1;
 75             memset(vis,0,sizeof(vis));
 76         }
 77         vis[idx(sa[i])]++;
 78     }
 79     int cnt=0;
 80     for(int j=1;j<=n;j++) if(vis[j]) cnt++;
 81     if(cnt==n) return 1;
 82     return 0;
 83 }
 84
 85 int main()
 86 {
 87     freopen("a.in","r",stdin);
 88     int T;
 89     scanf("%d",&T);
 90     while(T--)
 91     {
 92         scanf("%d",&n);
 93         int num=123;
 94         cl=0;
 95         for(int i=1;i<=n;i++)
 96         {
 97             scanf("%s",s+1);
 98             sl=strlen(s+1);
 99             if(i>1) c[++cl]=++num;
100             st[i]=cl+1;for(int j=1;j<=sl;j++) c[++cl]=s[j];ed[i]=cl;
101             c[++cl]=++num;
102             fst[i]=cl+1;for(int j=sl;j>=1;j--) c[++cl]=s[j];fed[i]=cl;
103         }
104         if(n==1) {printf("%d\n",sl);continue;}
105         get_sa(300);
106         get_h();
107         // for(int i=1;i<=cl;i++) printf("%c",c[i]);printf("\n");
108         // for(int i=1;i<=cl;i++) printf("%d ",c[i]);printf("\n");
109         // for(int i=1;i<=cl;i++) printf("%d ",sa[i]);printf("\n");
110         // for(int i=1;i<=cl;i++) printf("%d ",rk[i]);printf("\n");
111         // for(int i=1;i<=cl;i++) printf("%d ",h[i]);printf("\n");
112         int l=0,r=cl,mid;
113         while(l<r)
114         {
115             mid=(l+r+1)/2;
116             if(check(mid)) l=mid;
117             else r=mid-1;
118         }
119         printf("%d\n",l);
120     }
121     return 0;
122 }

这一题我曾经用kmp暴力水过。。贴一下代码

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<algorithm>
 7 using namespace std;
 8
 9 const int N=110,Inf=(int)1e9;
10 char s[N][N],c1[N],c2[N];
11 int n,id,cl,l[N],nt[N],nxt[N];
12
13 void kmp_nt()
14 {
15     nt[1]=0;
16     for(int i=2,p=0;i<=cl;i++)
17     {
18         while(c1[p+1]!=c1[i] && p) p=nt[p];
19         if(c1[p+1]==c1[i]) p++;
20         nt[i]=p;
21     }
22
23     nxt[1]=0;
24     for(int i=2,p=0;i<=cl;i++)
25     {
26         while(c2[p+1]!=c2[i] && p) p=nxt[p];
27         if(c2[p+1]==c2[i]) p++;
28         nxt[i]=p;
29     }
30 }
31
32 bool kmp_td(int x)
33 {
34     for(int i=1,p=0;i<=l[x];i++)
35     {
36         while(c1[p+1]!=s[x][i] && p) p=nt[p];
37         if(c1[p+1]==s[x][i]) p++;
38         if(p==cl) return 1;
39     }
40     for(int i=1,p=0;i<=l[x];i++)
41     {
42         while(c2[p+1]!=s[x][i] && p) p=nxt[p];
43         if(c2[p+1]==s[x][i]) p++;
44         // td[i]=p;
45         if(p==cl) return 1;
46     }
47     return 0;
48 }
49
50 bool check(int len)
51 {
52     for(int i=1;i+len-1<=l[id];i++)
53     {
54         cl=0;
55         for(int j=i;j<=i+len-1;j++)
56             c1[++cl]=s[id][j],c2[len-cl+1]=s[id][j];
57         kmp_nt();
58         bool bk=1;
59         for(int j=1;j<=n;j++)
60             if(!kmp_td(j)) {bk=0;break;}
61         if(bk) return 1;
62     }
63 }
64
65 int main()
66 {
67     freopen("a.in","r",stdin);
68     freopen("vio.out","w",stdout);
69     int T;
70     scanf("%d",&T);
71     while(T--)
72     {
73         scanf("%d",&n);
74         int ll=0,rr=Inf,mid;
75         for(int i=1;i<=n;i++)
76         {
77             scanf("%s",s[i]+1);
78             l[i]=strlen(s[i]+1);
79             if(l[i]<rr) rr=l[i],id=i;
80         }
81         while(ll<rr)
82         {
83             mid=(ll+rr+1)/2;
84             if(check(mid)) ll=mid;
85             else rr=mid-1;
86         }
87         printf("%d\n",ll);
88     }
89     return 0;
90 }
时间: 2024-10-10 21:19:25

【poj1226-出现或反转后出现在每个串的最长公共子串】后缀数组的相关文章

POJ 1226后缀数组:求出现或反转后出现在每个字符串中的最长子串

思路:这题是论文里的最后一道练习题了,不过最后一题竟然挺水的. 因为求的是未反转或者反转后,最长公共子串. 刚开始还真不知道怎么构建连接成一个字符串,因为需要有反转嘛! 但是其实挺简单的,把未反转的和反转后的字符串都连起来,中间用未出现过的字符隔开就行了!然后未反转的和反转的在同一组. 二分枚举最长的公共前缀长度,然后统计看看这个最长的长度在不在所有的组里,如果在就符合-- #include<iostream> #include<cstdio> #include<cstrin

Java算法——求出两个字符串的最长公共字符串

问题:有两个字符串str1和str2,求出两个字符串中最长公共字符串. 例如:“acbbsdef”和"abbsced"的最长公共字符串是“bbs” 算法思路: 1.把两个字符串分别以行和列组成一个二维矩阵. 2.比较二维矩阵中行和列对应的每个点的字符是否相同,是设置这个点为1,否设置这个点为0. 3.通过查找值为1的最长对角线来找到最长公共字符串. 通过上面str1和str2两个字符串,分别得出以行和列组成的一个二维矩阵如下图: 从上图可以看到,str1和str2共有3个公共子串&qu

POJ 3294 出现在至少K个字符串中的子串

在掌握POJ 2774(两个串求最长公共子串)以及对Height数组分组后,本题还是容易想出思路的. 首先用字符集外的不同字符连接所有串,这是为了防止两个后缀在比较时超过某个字符串的分界.二分子串的长度,扫描height数组,判定是否有某个分组来源与至少K个原字符串(本题要求出现超过n的一半次). #include <iostream> #include <vector> #include <algorithm> #include <string> #inc

[POJ1226]Substrings(后缀数组)

传送门 给定 n 个字符串,求出现或反转后出现在每个字符串中的最长子串. 算法分析: 这题不同的地方在于要判断是否在反转后的字符串中出现.其实这并没有加大题目的难度. 只需要先将每个字符串都反过来写一遍,中间用一个互不相同的且没有出现在字符串中的字符隔开, 再将 n 个字符串全部连起来,中间也是用一个互不相同的且没有出现在字符串中的字符隔开,求后缀数组. 然后二分答案,再将后缀分组. 判断的时候,要看是否有一组后缀在每个原来的字符串或反转后的字符串中出现. 这个做法的时间复杂度为 O(nlogn

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

写出一个程序,接受一个字符串,然后输出该字符串反转后的字符串。

题目描述 写出一个程序,接受一个字符串,然后输出该字符串反转后的字符串.例如: 输入描述: 输入N个字符 输出描述: 输出该字符串反转后的字符串 输入例子: abcd 输出例子: dcba import java.util.Scanner; public class Main {     public static void main(String[] args) {         // TODO Auto-generated method stub         Scanner in = 

array_flip() 函数返回一个反转后的数组

定义和用法 array_flip() 函数返回一个反转后的数组,如果同一值出现了多次,则最后一个键名将作为它的值,所有其他的键名都将丢失. 如果原数组中的值的数据类型不是字符串或整数,函数将报错 array_unique() 函数移除数组中的重复的值,并返回结果数组. 当几个数组元素的值相等时,只保留第一个元素,其他的元素被删除. 返回的数组中键名不变. 在PHP中,用于删除数组中重复元素有一个可用的函数,那就是 array_unique(), 但是它并不是一个最高效的方法,使用array_fl

题目:输入一个链表的头结点,反转该链表,并返回反转后链表的头结点。

题目:输入一个链表的头结点,反转该链表,并返回反转后链表的头结点.链表结点定义如下: struct ListNode { int       m_nKey; ListNode* m_pNext; }; c语言实现 /* File : rlink.c Author : Date : 2015/4/4 platform : windows7 x86_64 version : 1.0 Function : 反转一个链表 */ #include <stdio.h> #include <stdli

调用百度地图API的应用混淆后出问题

1 混淆后出问题,程序异常退出 在proguard-project.txt中添加 -libraryjars libs/BaiduLBS_Android.jar -keep class com.baidu.** { *; } -keep class vi.com.gdi.bgl.android.**{*;} 2 程序可以打开,但地图界面不显示地图图层,只显示网格 原因:APK混淆后数字签名发生变化 解决:使用keytool -list -v -keystore xxx 察看混淆时生成的KEY文件中