竞赛讨论区 > E-Sort String 字符串hash+hash表做法
头像
Thanks_up
编辑于 2018-07-27 10:04
+ 关注

E-Sort String 字符串hash+hash表做法

这题一开始就想到了要O(1)得到起始点为i(0~len-1)长度为len的每一个串的hash值,但是在这之后如果不能O(n)的在容器里将他们分类排序的话基本上就超时(sort排序超了凉凉);
用字符串hash将翻倍了的字符串hash一遍,然后O1得到每一个len长的串的hash值,将每一个很大的hash值用hash表存起来分配到vector里输出;
这题用了双hash,hash值里用到的INF如果是1e9的话还是有可能hash冲突的(就比如说我这个非洲人就wa了,别人交就过了。)


///耗时:817ms(菜鸡的代码,写的很挫)  内存使用:152288kb
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6+1e5+7, w=26;
const ll INF=2e9;///这个值为1e9可能还会有冲突
void write(int x){
    if(x==0){putchar(48);return;}
    int len=0,dg[20];
    while(x>0){dg[++len]=x%10;x/=10;}
    for(int i=len;i>=1;i--)putchar(dg[i]+48);
}
int len, cnt;
struct has
{
    ll P;
    ll mi_w[maxn];
    ll hs[maxn];
    void init(char *s)
    {
        P=1e9+rand();
        mi_w[0]=1;
        for(int i=1; i<=len; i++)
            mi_w[i]=mi_w[i-1]*w%P;
        for(int i=1; i<=len; i++)
            hs[i]=(hs[i-1]*w+s[i]-'0')%P;
    }
    ll get(int l,int r)
    {
        return ((hs[r]-hs[l-1]*mi_w[r-l+1])%P+P)%P;
    }
    bool ok(int l,int r)
    {
        return (get(1,l)+get(l+1,r)-get(r+1,len))%P==0;
    }
}h[2];
const int Size=2333237;///hash表用的mod
struct Hash{
    int num[Size];
    ll key[Size];
    void init(){
        memset(num, 0, sizeof(num));
        memset(key, -1, sizeof(key));
    }
    int Insert(ll x){
        int num1=x%Size;
        while(key[num1]!=-1){
            if(key[num1]==x){
                return num[num1];
            }
            ++num1;
            if(num1>=Size)
                num1-=Size;
        }
        key[num1]=x;
        num[num1]=cnt++;
        return num[num1];
    }
}th;
char str[maxn];
vector<int> vv[maxn/2];
int main(){
    register int j, i, slen, top, sav;
    register ll aaa;
    srand(time(0));
    scanf("%s", str+1);
    len=slen=strlen(str+1);
    th.init();
    len*=2;
    for(i=1;i<=slen;++i)///翻倍
        str[i+slen]=str[i];
    h[0].init(str);
    h[1].init(str);
    cnt=0;
    for(i=1; i<=slen; ++i){
        aaa=h[0].get(i, i+slen)*INF+h[1].get(i, i+slen);
        sav=th.Insert(aaa);///插入的时候如果插入过这个数字就返回保存的下标值,没插入过就返回新保存的下标值
        vv[sav].push_back(i-1);
    }
    write(cnt);
    putchar('\n');
    for(i=0; i<cnt; ++i){
        sav=vv[i].size();
        write(sav);
        for(j=0; j<sav; ++j)
            putchar(' '), write(vv[i][j]);
        putchar('\n');
    }
}
用next数组找循环节的方法刚刚补了一下,果然快了好多;

耗时:138ms   内存使用:14056kb 
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+7;
int Next[N];
void write(int x){
    if(x==0){putchar(48);return;}
    int len=0,dg[20];
    while(x>0){dg[++len]=x%10;x/=10;}
    for(int i=len;i>=1;i--)putchar(dg[i]+48);
}
void getNext(char *s, int &len){
    Next[0]=-1;
    register int i=0,k=-1;
    while(i<len){
        if(k==-1||s[i]==s[k]){
            Next[++i]=++k;
        }
        else{
            k=Next[k];
        }
    }
}
char str[N];
int main(){
    register int i, j, len, cnt;
    scanf("%s", &str);
    len=strlen(str);
    getNext(str, len);
    if(len%(len-Next[len])==0&&Next[len]){
        cnt=len-Next[len];
        write(len-Next[len]);
        putchar('\n');
        for(i=0; i<cnt; ++i){
            write(len/(len-Next[len]));
            for(j=i; j<len; j+=cnt){
                putchar(' ');
                write(j);
            }
            putchar('\n');
        }
    }else{
        write(len);
        putchar('\n');
        for(i=0; i<len; ++i){
            putchar('1');
            putchar(' ');
            write(i);
            putchar('\n');
        }
    }
}

全部评论

(0) 回帖
加载中...
话题 回帖

等你来战

查看全部

热门推荐