zoukankan      html  css  js  c++  java
  • Repeats SPOJ

    题目链接
    题意:给定一个字符串,求重复次数最多的连续重复子串
    题目思路:先穷举长度L,然后求长度为L的子串最多能连续出现几次。首先连续出现1次是肯定可以的,所以这里只考虑至少2次的情况。假设在原字符串中连续出现2次,记这个子字符串为S,那么S肯定包括了字符r[0], r[L], r[L2],r[L3], ……中的某相邻的两个。所以只须看字符r[Li]和r[L(i+1)]往前和
    往后各能匹配到多远,记这个总长度为K,那么这里连续出现了K/L+1次。最后看最大值是多少。
    穷举长度L的时间是n,每次计算的时间是n/L。所以整个做法的时间复杂度是O(n/1+n/2+n/3+……+n/n)=O(nlogn)。
    用RMQ预处理区间最小值,方便求r[Li]和r[L(i+1)]的最长公共前缀,这就是往后面匹配了多长。
    对于往前面匹配了多长,我们只要看jL-(i-k%i)和(j+1)L-(i-k%i)能否算上去即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    #define Max_N 50010
    #define F(x) ((x)/3+((x)%3==1?0:tb))
    #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2) 
    int wa1[Max_N*3], wb1[Max_N*3], wv1[Max_N*3], ws1[Max_N*3]; 
    int c0(int *r, int a, int b)  
    {
    	return r[a] == r[b] && r[a+1] == r[b+1] && r[a+2] == r[b+2];
    }
     
    int c12(int k, int *r, int a, int b) 
    {
    	if(k == 2) return r[a] < r[b] || r[a] == r[b] && c12(1, r, a+1, b+1); 
        else return r[a] < r[b] || r[a] == r[b] && wv1[a+1] < wv1[b+1];
    }
        
     void sort(int *r, int *a, int *b, int n, int m) 
     { 
    	int i; 
    	for (i = 0; i < n; i++) wv1[i] = r[a[i]]; 
    	for (i = 0; i < m; i++) ws1[i] = 0; 
    	for (i = 0; i < n; i++) ws1[wv1[i]]++; 
    	for (i = 1; i < m; i++) ws1[i] += ws1[i-1]; 
    	for (i = n-1; i >= 0; i--) b[--ws1[wv1[i]]] = a[i]; 
    	return; 
     }
    void dc3(int *r, int *sa, int n, int m)
    {
    	int i, j, *rn = r + n, *san = sa + n,ta = 0,tb = (n + 1) / 3,tbc = 0,p; 
        r[n] = r[n+1] = 0; 
        for (i = 0; i < n; i++) if (i % 3 != 0) wa1[tbc++] = i; 
        sort(r+2, wa1, wb1, tbc, m); 
        sort(r+1, wb1, wa1, tbc, m); 
        sort(r, wa1, wb1, tbc, m); 
        for (p = 1,rn[F(wb1[0])] = 0,i = 1;i < tbc; i++) 
        rn[F(wb1[i])] = c0(r, wb1[i-1], wb1[i]) ? p - 1 : p++; 
        if (p < tbc) dc3(rn, san, tbc, p); 
        else for (i = 0; i < tbc; i++) san[rn[i]] = i;
        for (i = 0; i < tbc; i++) if (san[i] < tb) wb1[ta++] = san[i] * 3; 
        if (n % 3 == 1) wb1[ta++] = n - 1; 
        sort(r, wb1, wa1, ta, m); 
        for (i = 0; i < tbc; i++) wv1[wb1[i] = G(san[i])] = i; 
        for (i = 0, j = 0, p = 0; i < ta && j < tbc; p++) 
        sa[p] = c12(wb1[j] % 3, r, wa1[i], wb1[j]) ? wa1[i++] : wb1[j++]; 
        for(; i < ta; p++) sa[p] = wa1[i++]; 
        for(; j < tbc; p++) sa[p] = wb1[j++]; 
        return; 
    }
    // str sa 都要开三倍的;
    void da(int str[], int sa[], int rank1[], int height1[], int n, int m)
    {
    	for (int i = n; i < n*3; i++)
    		str[i] = 0;
    	dc3(str, sa, n+1, m);
    	int i, j, k = 0;
    	for (i = 0; i <= n; i++) rank1[sa[i]] = i;
    	for (i = 0; i < n; i++)
    	{
    		if (k) k--;
    		j = sa[rank1[i] - 1];
    		while (str[i+k] == str[j+k]) k++;
    		height1[rank1[i]] = k;
    	}
    	return;
    }
    int a[Max_N];
    int str[3*Max_N];
    int sa[3*Max_N];//sa[i]表示将所有后缀排序后第i小的后缀的编号。
    int rank1[3*Max_N];//rank1[i]表示后缀i的排名。
    int height1[3*Max_N]; //LCP(sa[i],sa[i-1]);
    int n;
    int minnum[Max_N][16];
    void RMQ()   		//预处理  O(nlogn)
    {
    	int i,j;
    	int m=(int)(log(n*1.0)/log(2.0));
    	for(i=1;i<=n;i++)
    		minnum[i][0]=height1[i];
    	for(j=1;j<=m;j++)
    		for(i=1;i+(1<<j)-1<=n;i++)
    			minnum[i][j]=min(minnum[i][j-1],minnum[i+(1<<(j-1))][j-1]);
    }
    int Ask_MIN(int a,int b) 	//O(1)
    {
    	int k=int(log(b-a+1.0)/log(2.0));
    	return min(minnum[a][k],minnum[b-(1<<k)+1][k]);
    }
    int calprefix(int a,int b)
    {
        a=rank1[a],b=rank1[b];
        if(a>b)
            swap(a,b);
        return Ask_MIN(a+1,b);
    }
    int main()
    {
    	int t;
    	scanf("%d",&t);
    	while(t--)
    	{
    		scanf("%d",&n);
    		char s[5];
    		for(int i=0;i<n;i++)
    		{
    			scanf("%s",s);
    			str[i]=s[0]-'a'+1;
    		}
    		str[n]=0;
    		da(str, sa, rank1, height1, n, 5);
    		RMQ();
    		int ans,Max=1;
    		for(int i=1;i<=n;i++)
            {
                for(int j=0;j+i<n;j+=i)
                {
                    ans=calprefix(j,j+i);
                    int k=j-(i-ans%i);
                    ans=ans/i+1;
                    if(k>=0&&calprefix(k,k+i)>=i)
                        ans++;
                    Max=max(Max,ans);
                }
            }
            printf("%d
    ",Max);
    	 } 
     }       
    
  • 相关阅读:
    hdu 3996
    poj 3189
    poj 2391
    zoj 3165
    【Visual Studio】
    httpwebrequest Winform 上传图片
    [MVC] win7 下 配置 IIS 问题
    win7 下 升级 vs2008
    [Visual Studio 2010] NET 4.0 WinForm无法引用System.Web.dll的解决方法
    [XML] XML
  • 原文地址:https://www.cnblogs.com/2462478392Lee/p/13810828.html
Copyright © 2011-2022 走看看