POJ 3294 二分找超过一半字符串中存在的子串

题目大意:

给定n个字符串,求出现在不小于k/2个字符串中的最长子串。

二分找对应子串长度的答案,将所有字符串链接成一个长字符串求后缀数组,记录每一个位置本属于第几个字符串,利用height查询的时候,

根据记录的位置不断判断是否出现重复的字符串是在同一个字符串内的

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <vector>
  4 #include <iostream>
  5 using namespace std;
  6 typedef long long ll;
  7 const int N = 200010;
  8 int r[N] , sa[N] , rank[N] , height[N];
  9 int K , wa[N] , wb[N] , wv[N] , wsf[N];
 10 int cmp(int *r , int a , int b , int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
 11 void da(int *r , int *sa , int n , int m)
 12 {
 13     int i,j,p,*x=wa,*y=wb,*t;
 14     for(i=0;i<m;i++)wsf[i]=0;
 15     for(i=0;i<n;i++)wsf[x[i]=r[i]]++;
 16     for(i=1 ; i<m ; i++) wsf[i]+=wsf[i-1];
 17     for(i=n-1;i>=0;i--) sa[--wsf[x[i]]]=i;
 18     for(j=1,p=1;p<n;j*=2,m=p){
 19         for(p=0,i=n-j;i<n;i++) y[p++]=i;
 20         for(i=0;i<n;i++) if(sa[i]>=j)y[p++]=sa[i]-j;
 21         for(i=0;i<n;i++) wv[i]=x[y[i]];
 22         for(i=0;i<m;i++) wsf[i]=0;
 23         for(i=0;i<n;i++) wsf[wv[i]]++;
 24         for(i=1;i<m;i++) wsf[i]+=wsf[i-1];
 25         for(i=n-1;i>=0;i--) sa[--wsf[wv[i]]]=y[i];
 26         for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
 27             x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
 28     }
 29     return;
 30 }
 31 void callHeight(int *r , int *sa , int n)
 32 {
 33     int i,j,k=0;
 34     for(i=1;i<=n;i++) rank[sa[i]]=i;
 35     for(i=0;i<n;height[rank[i++]]=k)
 36         for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
 37     return;
 38 }
 39 #define ll long long
 40 int n , len , pos[105][2] , dif , mp[N];
 41 bool vis[105];
 42 char s[105][1005] , all[N];
 43 vector<int> ans , tmp;
 44 bool check(int mid)
 45 {
 46     tmp.clear();
 47     bool flag = false;
 48     memset(vis , 0 , sizeof(vis));
 49     int cnt = 1 , rec = sa[0];
 50     vis[sa[0]] = true;
 51     for(int i=1 ; i<len ; i++){
 52         if(height[i]<mid){
 53             if(cnt>n/2){
 54                 tmp.push_back(rec);
 55                 flag = true;
 56             }
 57             memset(vis , 0 , sizeof(vis));
 58             cnt = 1 , vis[mp[sa[i]]] = true , rec = sa[i];
 59         }
 60         else{
 61             if(!vis[mp[sa[i]]]){
 62                 vis[mp[sa[i]]] = true;
 63                 cnt++;
 64                 rec = sa[i];
 65             }
 66         }
 67     }
 68     if(flag) ans = tmp;
 69     return flag;
 70 }
 71 int bin_search()
 72 {
 73     int l=0 , r=1000 , ans=0 , mid;
 74     while(l<=r){
 75         mid = (l+r)>>1;
 76         if(check(mid)) l=mid+1 , ans=mid;
 77         else r=mid-1;
 78     }
 79     return ans;
 80 }
 81 int main()
 82 {
 83    // freopen("a.in" , "r" , stdin);
 84     bool flag = false;
 85     while(scanf("%d" , &n) , n){
 86         if(flag) puts("");
 87         flag = true;
 88         len = 0 , dif = 27;
 89         for(int i=0 ; i<n ; i++){
 90             scanf("%s" , s[i]);
 91             for(int j=0 ; j<strlen(s[i]) ; j++) all[len] = s[i][j] , mp[len]=i+1 , r[len++] = s[i][j]-‘a‘+1;
 92             pos[i][0] = len;
 93             all[len] = ‘*‘;
 94             mp[len]=i+1 , r[len++] = dif++;
 95         }
 96         r[len-1] = 0;
 97         da(r , sa , len , dif);
 98        // for(int i=0 ; i<len ; i++) cout<<"i: "<<i<<" "<<sa[i]<<endl;
 99         callHeight(r , sa , len-1);
100         int ret = bin_search();
101         if(!ret) puts("?");
102         else{
103             for(int i=0 ; i<ans.size() ; i++){
104                 for(int j=ans[i] , t=0 ; t<ret ; j++ , t++) printf("%c" , all[j]);
105                 puts("");
106             }
107         }
108     }
109 }
时间: 2024-10-01 02:56:00

POJ 3294 二分找超过一半字符串中存在的子串的相关文章

找出字符串中第一个只出现一次的字符

find the first unique character in  a string and you can just traverse this string only one time. if there is no such character, just return '#' and '#' will not appear in the string, else return the character you find. for example: "aAbBABac",

c语言代码编程题汇总:找出字符串中与输入的字母元素相同的个数以及其所对应数组的下标值

找出字符串中与输入的字母元素相同的个数以及其所对应数组的下标值 程序代码如下: 1 /* 2 2017年3月8日08:39:16 3 功能:找出字符串中与输入的字母元素相同的个数以及其所对应数组的下标值 4 */ 5 6 #include"stdio.h" 7 int main (void) 8 { 9 int i = 0, j = 0; 10 char a[100]; 11 char ch; 12 int num = 0; 13 14 printf ("please inp

找出字符串中出现次数最多的字符,和最大次数

/*找出字符串中出现次数最多的字符,和最大次数*/ function countMax(str){ var max = 0; // 记录出现的最大次数 var maxChar = ""; // 记录出现最多次数的字符 var counts = new Array(127); // 记录中间计算结果 for(var i = 0; i < counts.length; i++){ counts[i] = 0; } for(var i = 0; i < str.length; i

找出字符串中第一个不重复的字符(JavaScript实现)

如题~ 此算法仅供参考,小菜基本不懂高深的算法,只能用最朴实的思想去表达. 1 //找出字符串中第一个不重复的字符 2 // firstUniqueChar("vdctdvc"); --> t 3 function firstUniqueChar(str){ 4 var str = str || "", 5 i = 0, 6 k = "", 7 _char = "", 8 charMap = {}, 9 result =

华为OJ:找出字符串中第一个只出现一次的字符

可以稍微让代码写的好看,不用直接写双循环的话,就可以写成函数的调用,重用性也很高. import java.util.Scanner; public class findOnlyOnceChar { public static boolean FindChar(String pInputString, char pChar){ int count=0; for(int i=0;i<pInputString.length();i++){ if(pInputString.charAt(i)==pCh

找出字符串中第一个出现次数最多的字符

找出字符串中第一个出现次数最多的字符 详细描述: 接口说明 原型: bool FindChar(char* pInputString, char* pChar); 输入参数: char* pInputString:字符串 输出参数(指针指向的内存区域保证有效): char* pChar:出现次数最多的字符 返回值: false 异常失败 true  输出成功 #include <iostream> #include <string.h> using namespace std; b

黑马程序员——找出两个字符串中最大的子串

找出两个字符串中最大的子串 </pre><pre name="code" class="java">public class StringMaxString { //找一个字符串的最大子串 public static void main(String[] args) { // TODO Auto-generated method stub String s1="qwerabcdtyuiop"; String s2=&quo

获取两个字符串中最大相同子串

2.获取两个字符串中最大相同子串.第一个动作:将短的那个串进行长度一次递减的子串打印. "cvhellobnmtanop" "andefc" 思路: 1,将短的那个子串按照长度递减的方式获取到. 2,将每获取到的子串去长串中判断是否包含,如果包含,已经找到! package tan; class Test { public static String getMaxSubString(String s1,String s2) { String max = "

HDU 4622 求解区间字符串中的不同子串的个数

题目大意: 给定一个长度<2000的串,再给最多可达10000的询问区间,求解区间字符串中的不同子串的个数 这里先考虑求解一整个字符串的所有不同子串的方法 对于后缀自动机来说,我们动态往里添加一个字符,每次添加一个字符进去,我们只考虑那个生成的长度为当前长度的后缀自动机的节点 那么这个节点可接收的字符串的个数就是( p->l - p->f->l ),也就是以当前点为最后节点所能得到的与之前不重复的子串的个数 那么这个问题就很好解决了,共2000个位置,以每一个位置为起点构建一次后缀