POJ-3294 Life Forms 哈希

这题的题意是给定N个串,某个串的子串在超过一半的串存在的话,那么这个串就是可取的,问满足这样的子串的最长长度是多少,如果有多个的话,按字典序输出。

这题我是用hash过的。大体步骤是这样的,首先保留最长串的长度,然后二分0-MAXNLEN得到答案,那么这里重点就是如何去写这个判定函数。二分里面只会传递一个参数那就是长度K,根据这个长度我们把所有的串都拆成长度为K的子串,这里有个优化就是使用一种hash规则能够在得到1 - N的hash的时候计算出2 - N+1的hash值,那么这里用到了经典的多项式插值取模的方法:假设有一个字符串a[0],a[1],a[2],a[4],取长度为3的子串的时候,第一个子串的hash值是 a[0]*T^2 + a[1]*T + a[2] 第二个子串就是 a[1]*T^2 + a[2]*T+a[3], 这样一次for循环遍历就可以完成一个串的所有子串的hash了,具体就是先减去最前面的一项乘以后面的T的次方,整个KEY值乘以T,再加上后面的一项就可以了。这样的话,下次再判定是否为同一个串的时候只需要进入KEY%MOD的链表中寻找KEY值相同的串即可了,无须判定原始的字符串。

这里还有一个小优化就是二分答案的时候并不需要找完所有的子串,只要存在就可以退出了,最后一次查找就需要全部存储了。

代码如下:

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>
#include <vector>
#include <string>
#include <iostream> 
#define T 113
#define MOD 3001
using namespace std;

typedef unsigned long long UInt64;

struct Node
{
    UInt64 key;
    int NO, idx, cnt;
}info;

int N;

UInt64 mypow[1005];

char s[105][1005], temp[1005];

vector<string>vs;

vector<Node>val[MOD]; // 同一hash值的不同初始值 

vector<string>::iterator it;

bool Hash(int NO, int idx, UInt64 key, int k, int f)
{
    int Rkey = key % MOD, flag = 0;
    for (int i = 0; i < (int)val[Rkey].size(); ++i) {
        if (key == val[Rkey][i].key) {  // 说明这个串已经在这一轮中出现过
            flag = 1;
            if (NO != val[Rkey][i].NO) {  // 说明这是一个后加入的串 
                val[Rkey][i].NO = NO;
                val[Rkey][i].idx = idx;
                ++val[Rkey][i].cnt; 
                if (val[Rkey][i].cnt > (N>>1) && !f) {
                     return true;
                }
            }
        }
    }
    if (!flag) {
        info.cnt = 1, info.key = key;
        info.NO = NO, info.idx = idx;
        val[Rkey].push_back(info);
    }
    return false;
}

bool judge(int k, int f)
{
    int lim = N>>1;
    for (int i = 0; i < MOD; ++i) {  // 便利所有的Rkey 
        for (int j = 0; j < (int)val[i].size(); ++j) {
            if (val[i][j].cnt > lim) {
                if (!f) {
                    return true;
                }
                else {
                    memcpy(temp, s[val[i][j].NO]+val[i][j].idx, sizeof (char) * k);
                    temp[k] = '\0';
                    vs.push_back(temp);
                }
            }
        }
    }
    return false;
} 

void init()
{
    for (int i = 0; i < MOD; ++i) {
        val[i].clear();
    }    
}

inline bool Accept(int k, int f)
{
    int length;
    UInt64 key;
    init();
    for (int i = 1; i <= N; ++i) { 
        length = strlen(s[i]);
        if (length < k)    continue; // 长度小于k直接跳过 
        key = 0;
        for (int j = 0; j < k; ++j) {
            key = key * T + s[i][j];
        }
        if (Hash(i, 0, key, k, f) && !f) {
            return true;
        }
        for (int j = 1; j < length-k+1; ++j) { 
            key -= s[i][j-1] * mypow[k-1];
            key = key * T + s[i][j+k-1];
            if (Hash(i, j, key, k, f) && !f) {
                return true;
            }
        }
    }
//    puts("I am here~~");
    if (!f) {
        return false;
    }
    else {
        return judge(k, f);
    }
}

int bsearch(int l, int r)
{
    int mid;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (Accept(mid, 0)) { // 这个长度可接受的话,那么就把长度变长
            l = mid + 1;
        }
        else {
            r = mid - 1;
        } 
    }
    return r; // r 一定是满足要求的最大解
}

int main()
{
    int MaxL, len, ans, first = 1;
    mypow[0] = 1;
    for (int i = 1; i <= 1000; ++i) {
        mypow[i] = mypow[i-1]*T; // unsigned 拥有这个自动对上界取模的特性 
    }
    while (scanf("%d", &N), N) {
        vs.clear();
        if (first) {  // 对空行的处理
            first = 0;
        }
        else {
            puts("");
        }
        MaxL = 0;
        for (int i = 1; i <= N; ++i) {
            scanf("%s", s[i]);
            len = strlen(s[i]);
            MaxL = max(MaxL, len); // 得到最长的串的长度进行二分
        }
        ans = bsearch(0, MaxL);
        if (ans <= 0) {
            puts("?");
        }
        else { 
            
            Accept(ans, 1);
            sort(vs.begin(), vs.end());
            for (it = vs.begin(); it != vs.end(); ++it) {
                cout << *it << endl;
            }
        }
    }
    return 0;
}
posted @ 2012-07-18 21:22  沐阳  阅读(267)  评论(0编辑  收藏  举报