TZOJ3043: 取个标题好难 最长的出现次数>=k的不重复子串长度

3043: 取个标题好难 分享至QQ空间

Time Limit(Common/Java):6000MS/18000MS     Memory Limit:65536KByte
Total Submit: 17            Accepted:4

Description

你是否经常在写完文章之后为文章取一个合适的标题而苦恼?这里提供一个很有趣的方法。
首先,标题应该概括文章的内容。为了简单起见,如果一个短语在文章中不重叠地出现了至少k次,那么这个短语就可以作为标题。例如,”dadadad”中,短语”dad” 不重叠地出现了两次,而不是三次。
其次,标题越长越好。长标题才能吸引眼球。
最后,这件事应该让计算机来做比较省事。
所以,请编写一个程序,根据给定的k和一段文章,给这篇文章取个标题。

Input

输入包括多组数据。
每组数据第一行为整数k,第二行为一个字符串表示文章内容,为了简化问题,字符串仅含小写字母。字符串长度不超过10000。
输入数据以k=0结束。

Output

对每组数据输出最长的标题长度,如果找不到符合要求的标题,则输出0。

Sample Input

3
abababa
1
apple
4
abababa
5
abababa
0

Sample Output

2
5
1
0

最长的出现次数>=k的不重复子串长度,这个题目明显可以hash,然后在二分长度,排序的时候将位置也标记下,最后判断下就可以了

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ulint;
#define maxn 23333
const ulint x=233;
int M;
char in[maxn];
ulint has[maxn];
int pos[maxn];
int id[maxn];
ulint xp[maxn];
ulint H[maxn];
bool cmp(int a,int b)
{
    return has[a]<has[b]||has[a]==has[b]&&a<b;
}
bool check(int mid,int n)
{
    pos[mid]=0;
    id[0]=0;
    for(int i=0; i+mid-1<n; i++)//就算每一段的hash值
        id[i]=i,has[i]=H[i]-H[i+mid]*xp[mid];
    sort(id,id+n-mid+1,cmp);//按hash值排序
    int cnt=1;
    bool ret=0;
    int tmp=id[0];
    for(int i=1; i<=n-mid; i++)
        if(has[id[i]]==has[id[i-1]])
        {
            if(id[i]-tmp>=mid)
            cnt++,tmp=id[i];
            if(cnt>=M)
                pos[mid]=max(pos[mid],tmp),ret=1;
        }
        else
            cnt=1,tmp=id[i];
    return ret;
}
int main()
{
    xp[0]=1;
    for(int i=1; i<maxn; i++)//预处理x次幂项
        xp[i]=xp[i-1]*x;
    while(scanf("%d",&M),M)
    {
        scanf("%s",in);
        int n=strlen(in);
        if(M==1)
        {
            printf("%d\n",n);
            continue;
        }
        H[n]=0;
        for(int i=n-1; i >= 0; i--)//预处理H[i]=s[i]+s[i+1]*x+...+s[n]*x^(n-i)
            H[i]=H[i+1]*x+(in[i]-'a');
        int L=1,R=n,mid,len=0;
        while(L<=R)
        {
            mid=(L+R)>>1;
            if(check(mid,n))
            {
                len=mid;
                L=mid+1;
            }
            else
                R=mid-1;
        }
        if(len==0)
            printf("0\n");
        else
            printf("%d\n",len);
    }
    return 0;
}

然后也可以后缀数组,我们之前那个题允许重复,直接判断height即可,这个题目要保存sa[i],然后排序判断是否重复

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <set>
#define dbg(x) std::cout<<#x<<" = "<< (x)<< "\n"
#define maxn 20005
int wa[maxn],wb[maxn],wv[maxn],ws[maxn],m;
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void getsa(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++) ws[i]=0;
    for(i=0; i<n; i++) ws[x[i]=r[i]]++;
    for(i=1; i<m; i++) ws[i]+=ws[i-1];
    for(i=n-1; i>=0; i--) sa[--ws[x[i]]]=i;
    for(j=1,p=1; p<n; j*=2,m=p)
    {
        for(p=0,i=n-j; i<n; i++) y[p++]=i;
        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0; i<n; i++) wv[i]=x[y[i]];
        for(i=0; i<m; i++) ws[i]=0;
        for(i=0; i<n; i++) ws[wv[i]]++;
        for(i=1; i<m; i++) ws[i]+=ws[i-1];
        for(i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
    int i,j,k=0;
    for(i=1; i<=n; i++) rank[sa[i]]=i;
    for(i=0; i<n; height[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);
}
int a[maxn];
int check(int *sa,int n,int k)
{
    int i,cnt=1;
    a[0]=sa[1];
    for(i=2; i<=n; i++)
    {
        if(height[i]>=k)
        {
            a[cnt++]=sa[i];
        }
        else
        {
            if(cnt>=m)
            {
                std::sort(a,a+cnt);
                int x=a[0],tot=cnt;
                for(int i=1; i<tot; i++)
                {
                    if(a[i]-x<k)cnt--;
                    else x=a[i];
                }
                if(cnt>=m)return 1;
            }
            cnt=1,a[0]=sa[i];
        }
    }
    std::sort(a,a+cnt);
    int x=a[0],tot=cnt;
    for(int i=1; i<tot; i++)
    {
        if(a[i]-x<k)cnt--;
        else x=a[i];
    }
    if(cnt>=m)return 1;
    return 0;
}
int r[maxn],sa[maxn];
char str[maxn];
int main()
{
    //freopen("test.in","r",stdin);
    while(scanf("%d",&m),m)
    {
        scanf("%s",str);
        int n=strlen(str);
        if(m==1)
        {
            printf("%d\n",n);
            continue;
        }
        for(int i=0; i<n; i++)r[i]=str[i];
        r[n]=0;
        getsa(r,sa,n+1,300);
        calheight(r,sa,n);
        int L=1,R=n,mi,len=0;
        while(L<=R)
        {
            mi=(L+R)>>1;
            if(check(sa,n,mi))L=mi+1,len=mi;
            else R=mi-1;
        }
        printf("%d\n",len);
    }
    return 0;
}

 

 

posted @ 2019-04-01 07:48  暴力都不会的蒟蒻  阅读(319)  评论(0编辑  收藏  举报