zoukankan      html  css  js  c++  java
  • 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);
                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
    ");
            else
                printf("%d
    ",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)<< "
    "
    #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);
                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
    ",len);
        }
        return 0;
    }

     

  • 相关阅读:
    一个简单的禁止鼠标滚轮事件处理
    模仿抽奖转盘,并且用cookie记录历史次数
    学习jquery
    使用var提升变量声明
    Django 自带密码加密,自定密码加密方式 及自定义验证方式
    kindEditor使用并防止xss攻击(day88)
    python二维码生成库(qrcode)简介和实例
    js原型的区别
    js中 this与that
    python单元测试之unittest框架使用总结
  • 原文地址:https://www.cnblogs.com/BobHuang/p/10634339.html
Copyright © 2011-2022 走看看