hdu5510 kmp+二分

来源:互联网 发布:本机端口号查询 编辑:程序博客网 时间:2024/05/30 22:53
//给出n个字符串,找出最大的i//使得在(1<=j<i)的范围内有字符串不是i的子串//从n-1开始往前推,如果字符串j是字符串str[n]的子串//那么字符串j是在字符串n的哪个区间范围内//存下前面所有字符串在字符串n的所有区间//然后可以用二分找到第一个不包含这个区间的前面的区间#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std ;const int maxn = 2010 ;char str[maxn][maxn] ;int l[maxn] , r[maxn] , id[maxn] ;int Next[maxn] ;void get_next(int p){    int n = strlen(str[p]) ;    Next[0] = -1 ;    int j = 0 ;    int k = -1 ;    while(j < n)    {        if(k == -1 || str[p][j] == str[p][k]){           j++ ;           k++ ;           Next[j] = k;        }        else k = Next[k] ;    }}int kmp(int p , int x){    int n = strlen(str[x])  ;    int m = strlen(str[p]) ;    int i =0 , j = 0 ;    while(i < n && j < m)    {        if(j == -1 || str[p][j] == str[x][i])        i++ ,j++ ;        else j = Next[j] ;    }    return j == m ? i - j + 1 : -1 ;}int find(int x , int len){    int lx = 0 ;    int rx = len-1 ;    while(lx<=rx)    {        int mid = (lx + rx) >> 1 ;        if(r[mid] >= x)        lx = mid + 1 ;        else rx = mid - 1 ;    }    return lx ;}int main(){    //freopen("d:\\in.txt" , "r" , stdin) ;    int t ;    scanf("%d" , &t) ;    int cas = 0 ;    while(t--)    {        int n ;        scanf("%d" , &n) ;        int ans = -1 ;        for(int i = 1;i <= n;i++)        scanf("%s" , str[i]) ;        int len = 0 ;        for(int i = n-1;i >= 1;i--){            int x = strlen(str[i]) ;            get_next(i) ;            int pos = kmp(i,n) ;            if(pos == -1){               ans = n ;               break ;            }            int px = upper_bound(l,l+len,pos)-l ;            int py = find(pos+x-1,len);            int tmp = min(px , py) ;            if(tmp == len){               l[len] = pos ;               r[len] = pos+x-1 ;               id[len++] = i;            }            else                ans = max(ans , id[tmp]) ;        }        printf("Case #%d: %d\n" , ++cas , ans) ;    }    return 0 ;}

0 0
原创粉丝点击