哈希算法概述

哈希用于字符串处理(快速判断两个字符串是否相同)。先介绍一下哈希的思想:

abc=97p2+98p1+99p0 a b c = 97 ∗ p 2 + 98 ∗ p 1 + 99 ∗ p 0

abd=97p2+98p1+100p0 a b d = 97 ∗ p 2 + 98 ∗ p 1 + 100 ∗ p 0

abcd=97p3+98p2+99p1+100p0 a b c d = 97 ∗ p 3 + 98 ∗ p 2 + 99 ∗ p 1 + 100 ∗ p 0

显然我们相当于把字符串变成一个p进制的数(称作这个字符串的哈希值),然后如果这个p进制的数相同就可以确定这两个字符串是相同的。但是我们发现:这个数可能会很大,所以我们只能把这两个数进行取模,限制在int范围内。但是由于取模,哈希值相同的字符串不一定相同。不过在模很大的情况下,哈希值相同,两个字符串是大概率相同的。也就是说,如果只判断两个字符串,我们只用计算两个串的哈希值然后比较是否相同,wa的几率是 1100000000 1 100000000

但是还有一类问题,我们需要判断某一个串是否出现过。有一个很直接的想法就是把所有哈希值放到bool数组中,计算一个,判断这个哈希值是否出现过。但是这个时候精度问题就十分感人。这个时候我们就需要双哈希,我们取不同的p和不同的模数,对于一个串计算两个哈希值,如果这两个同时出现过,那么也可以判断这个串出现过了。

哈希的思想是很简单的,但是写法上有技巧(令人窒息,第一次考试写wa了)。

for(int j=0;jj++)  
    for(int j=0;jj++)  
       hash=(hash*p+a[j])%mod;

再看一看二维哈希。

二维哈希的姿势要奇怪一点,先把p的次方填满矩阵:
哈希算法概述_第1张图片

然后每个格子乘上该格子的数,然后求矩阵前缀和。之后我们就可以O1查询每个矩阵的哈希值(支持加减)。虽然左上角次数不同,我们把所有次数乘成相同的就行。

有双哈希判断是否出现。

矩阵(matrix)

时间限制: 1000 ms 内存限制: 131072 KB
提交数: 33 通过数: 10

题目描述

给出一个 n×m n × m 的矩阵。让你从中发现一个最大的正方形。使得这样子的正方形在矩阵 中出现了至少两次。输出最大正方形的边长。

输入

第一行两个整数 n n , m m 代表矩阵的长和宽;
接下来 n n 行,每行 m m 个字符(小写字母) ,表示矩阵。

输出

输出一个整数表示满足条件的最大正方形的边长。

输入样例

5 10
ljkfghdfas
isdfjksiye
pgljkijlgp
eyisdafdsi
lnpglkfkjl

输出样例

3

数据规模

对于 30%的数据, n,m100 n , m ≤ 100

对于 100%的数据, n,m500 n , m ≤ 500

正如上面的方法,附上代码:

#include  
#include  
#include  
using namespace std;  
struct lxy{  
    int next;  
    long long x;  
}b[250005];  

int head[100005];  
long long pr1=97,pr2=233,mod1=94271,mod2=19260819;  
int n,m,ans,cnt;  
long long p1[250005],p2[250005];  
long long hash1[505][505],hash2[505][505];  
char s[505][505];  

void add(int op,int ed)  
{  
    cnt++;  
    b[cnt].next=head[op];  
    b[cnt].x=ed;  
    head[op]=cnt;  
}  

bool check(int k)  
{  
    cnt=0;  
    memset(head,-1,sizeof(head));  
    memset(b,0,sizeof(b));  
    long long has1,has2;  
    for(int i=1;i<=n-k+1;i++)  
      for(int j=1;j<=m-k+1;j++)  
      {  
        has1=hash1[i+k-1][j+k-1]+hash1[i-1][j-1]-hash1[i-1][j+k-1]-hash1[i+k-1][j-1];  
        has2=hash2[i+k-1][j+k-1]+hash2[i-1][j-1]-hash2[i-1][j+k-1]-hash2[i+k-1][j-1];  
        has1=has1*p1[(n-i+1)*m-j]%mod1;  
        has2=has2*p2[(n-i+1)*m-j]%mod2;  
        has1%=mod1;has2%=mod2;  
        if(has1<0) has1+=mod1;  
        if(has2<0) has2+=mod2;  
        for(int p=head[has1];p!=-1;p=b[p].next)  
          if(b[p].x==has2)  
          {  
            return true;  
          }  
        add(has1,has2);  
      }  
    return false;  
}  

void er(int l,int r)  
{  
    int mid=(l+r+1)/2;  
    if(check(mid)) ans=mid;  
    if(l==r) return;  
    if(ans==mid) er(mid,r);  
    else  er(l,mid-1);  
}  

int main()  
{  
    scanf("%d%d",&n,&m);  
    long long t1=1,t2=1,ci=0;  
    for(int i=1;i<=n;i++)  
      scanf("%s",s[i]+1);  
    p1[0]=1;p2[0]=1;  
    for(int i=1;i<=n;i++)  
      for(int j=1;j<=m;j++)  
      {  
        t1=t1*pr1%mod1;t2=t2*pr2%mod2;  
        hash1[i][j]=t1;hash2[i][j]=t2;  
        p1[(i-1)*m+j]=t1;p2[(i-1)*m+j]=t2;  
      }  
    for(int i=1;i<=n;i++)  
      for(int j=1;j<=m;j++)  
      {  
        hash1[i][j]=hash1[i][j]*s[i][j]%mod1;  
        hash2[i][j]=hash2[i][j]*s[i][j]%mod2;  
      }  
    for(int i=1;i<=n;i++)  
      for(int j=1;j<=m;j++)  
      {  
        hash1[i][j]=(hash1[i][j-1]+hash1[i][j])%mod1;  
        hash2[i][j]=(hash2[i][j-1]+hash2[i][j])%mod2;  
      }  
    for(int i=1;i<=n;i++)  
      for(int j=1;j<=m;j++)  
      {  
        hash1[i][j]=(hash1[i-1][j]+hash1[i][j])%mod1;  
        hash2[i][j]=(hash2[i-1][j]+hash2[i][j])%mod2;  
      }  
    er(1,min(n,m));  
    cout<

你可能感兴趣的:(字符串处理)