题目
题解思路
因为字符串hash是从第一位往第N位乘 进制
所以第一位会被乘N次。
当我们要去 i 到 j 的hash值的时候
让hash[j]减去hash[i-1]*(j-i+1)*进制
相当与吧hash[i-1]加了(j-i+1)个 0 。这样一减就是 i 到 j 的hash值的实值了。
有点难理解
和普通数字的进制有点相反,多模拟几遍。
枚举每个相同的前缀,
这样就可以直接二分可以延展的最大的长度了。
根据可行性二分枚举。
最大长度减了一点还是可以拼接成他的子串。
属实巧妙
AC代码
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7 ;
const int N = 100010 ;
const int mi = 266 ;
long long a[N] ;
long long b[N] ;
void calc( string sk , long long p[] )
{
for (int i = 0 ; i < sk.size() ; i++ )
{
if ( i == 0 )
{
p[i] = (sk[i] - 'a' )%mod ;
}else
p[i] = ( p[i-1]*mi + sk[i] - 'a')%mod ;
}
}
long long qsm(long long di , long long mi )
{
long long res = 1 ;
while (mi)
{
if (mi&1)
res = res * di %mod ;
mi >>= 1 ;
di = di*di%mod ;
}
return res ;
}
long long calchash(long long a[] , int l , int r )
{
if ( l == 0 )
return a[r] ;
long long tmp ;
tmp = ( ( a[r] - a[l-1]*qsm(mi,r-l+1)%mod )%mod + mod )%mod ;
return tmp ;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
string s , t ;
cin >> s >> t ;
calc(s,a);
calc(t,b);
long long ans = 0 ;
int sl = s.size();
int tl = t.size();
for (int i = 0 ; i < sl ; i++ )
{
if ( s[i] != t[i] )
break ;
int l = 0 , r = sl - 1 ;
while ( l <= r )
{
int mid = (l+r)/2 ;
long long sh = a[mid] ;
long long th ;
if ( i + 1 + mid >= tl )
th = -1 ;
else
th = calchash(b,i+1,i+1+mid) ;
if ( sh == th )
l = mid + 1 ;
else
r = mid - 1 ;
}
ans += l ;
}
cout << ans << "\n" ;
return 0 ;
}