poj-2774 二分+字符串哈希+二分

给两个最多100000长度的字符串,求出他们的最长公共子串。

        最长公共子串存在单调性 -- 如果两个串存在长度为k的公共子串,那么必然存在长度0到k-1的公共子串。

利用其单调性,先二分公共子串长度len,再用O(n)时间算出两个串各自长度为len的子串哈希值,再用O(nlogn)时间对计算得到的值排序+二分查找若存在相同的哈希值,则存在长度为len的公共子串。

这样的做法总体复杂度为O(logn * nlogn),100000的数据能过。可以优化的地方就是,比较是否存在两个相同哈希值的时候,再进一次哈希,只需用O(n),这样总体复杂度就为O(logn * n)

哈希后二分 1000+ms

#include 
#include 
#include 
#include 
using namespace std;

#define maxn  100007

unsigned long long h[maxn];
unsigned long long h1[maxn];
unsigned long long h2[maxn];
unsigned long long p[maxn];

char s1[maxn];
char s2[maxn];
int len1, len2;

unsigned long long pow(int x, int y)
{
    if(y==1) return x;
    if(y&1) return pow(x, y/2)*pow(x,y/2)*x;
    else return pow(x,y/2)*pow(x,y/2);
}

int check(int len)  //检查长度为len的公共子串是否存在
{
    unsigned long long p = pow(133, len);

	for(int i = len; i <= len1; i++)    //算出字符串1中所有区间[i,i+len-1]的子串哈希值,保存并排序
	   h[i-len] = h1[i]-h1[i-len]*p ;
	sort(h, h+len1-len+1);

	for(int i = len; i <= len2; i++)
		if(binary_search(h, h+len1-len+1, h2[i]-h2[i-len]*p)) //算出字符串2中所有区间[i,i+len-1]的子串哈希值
			return 1;                                         //并在数组h中二分查找是否有相同的值,若有则存在长度为len的公共子串

	return 0;
}

int main()
{
	while(scanf("%s", s1)!=EOF){
		scanf("%s", s2);
        len1 = strlen(s1), len2 = strlen(s2);

		h1[0] = 0;
		for(int i = 1; i <= len1; i++)    //对字符串1的前缀进行哈希
			h1[i] = h1[i-1]*133+s1[i-1];

		h2[0] = 0;
		for(int i = 1; i <= len2; i++)    //对字符串2的前缀进行哈希
			h2[i] = h2[i-1]*133+s2[i-1];

		int l = 1, r = min(len1, len2)+1;
		int m;
		int ans = 0;
		while(l < r){                     //二分枚举最长公共子串长度
			m = l+(r-l)/2;

			if(check(m)){
                ans = m;
                l = m+1;
			}
			else r = m;
		}
		printf("%d\n", ans);
	}
	return 0;
}

     二分+哈希+哈希,时间上会快一点... c++ 313ms


#include 
#include 
#include 

using namespace std;

typedef unsigned int ull;
#define maxn 100005
#define mod 131011

struct hNode
{
    ull val[2];
    int next;
};
hNode h[maxn];
int first[mod+10], top;

void insert(hNode &a)
{
    h[top] = a;
    int pos = a.val[0]%mod;
    h[top].next = first[pos];
    first[pos] = top++;
}


struct String
{
    char s[maxn];
    ull val[2][maxn];
    int length;
};

String a, b;

ull base[2][maxn] = { {1,13}, {1,2}};

void hash(String &);
void init()
{
    for(int i = 0; i < 2; i++)
        for(int j = 2; j < maxn; j++)
        base[i][j] = base[i][j-1]*base[i][1];

    a.length = strlen(a.s);
    b.length = strlen(b.s);
    hash(a);
    hash(b);
}

void hash(String &str)
{
    for(int i = 0; i < 2; i++) str.val[i][0] = 0;

    for(int i = 0; i < 2; i++)
        for(int j = 1; j <= str.length; j++)
        str.val[i][j] = str.val[i][j-1]*base[i][1]+str.s[j-1];
}


int check(int len)
{
    hNode tmp;
    top = 0;
    memset(first, -1, sizeof(first));

    for(int j = len; j <= a.length; j++){
        for(int i = 0; i < 2; i++)
            tmp.val[i] = a.val[i][j]-a.val[i][j-len]*base[i][len];
        insert(tmp);
    }

    int cnt;
    for(int j = len; j <= b.length; j++){
        for(int i = 0; i < 2; i++)
            tmp.val[i] = b.val[i][j]-b.val[i][j-len]*base[i][len];

        int pos = tmp.val[0]%mod;
        for(int i = first[pos]; i != -1; i=h[i].next){
            cnt = 0;
            for(int j = 0; j < 2; j++)
                if(tmp.val[j] == h[i].val[j])
                cnt++;
            if(cnt==2) return 1;
        }
    }

    return 0;
}


int main()
{
    while(scanf("%s %s", a.s, b.s)!=EOF){
        init();
        int ans = 0;
        int l = 1, r = min(a.length, b.length)+1;
        int m;
        while(l < r){
            m = l+(r-l)/2;
            if(check(m)){
                ans = m;
                l=m+1;
            }
            else r = m;
        }

        printf("%d\n", ans);
    }
}


你可能感兴趣的:(哈希)