ural1486二维hash
http://acm.timus.ru/problem.aspx?space=1&num=1486
题意:
一个最大500*500的字符矩阵,求最大的两个相同的字符正方形。正方形可以有重叠部分但不能重合。
tip:
二分答案,二分查找正方形的边长,然后使用一个Hash表来判断该边长是否可行。Hash函数二维情况。时间复杂度是O(NMlog(N*M))的。检查是否产生了相同的Hash函数,如果存在相同的Hash函数,则认为该边长可行,否则即认为它不可行。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <map>
using namespace std;
typedef unsigned long long ui;
typedef long long LL;
const int maxn = 510;
const ui MOD = 1e5+7;
int n,m,ans,ans1x,ans2x,ans1y,ans2y;//n行m列
LL cnt;
char s[maxn][maxn];
ui P = 233,Q= 23333,nc[maxn],mc[maxn],h[maxn][maxn];
//P:列上的乘数,Q:行上的乘数
void init(){
ans =0;
nc[0] = 1;mc[0] = 1;
for(int i = 1 ;i <= n ; i++){
nc[i] = nc[i-1] * P;
//cout <<"nc"<<nc[i]<<endl;
mc[i] = mc[i-1] * Q;
}
for(int i = 1 ;i <= n ; i++){
scanf("%s",s[i]+1);
}
for(int i = 1; i <= n ; i++){
//cout<<s[i]+1<<endl;
for(int j = 1; j <= m ; j++){
h[i][j] = h[i][j-1]*Q+s[i][j]-'a'+1;//一行的
}
}
for(int i = 2; i <= n ; i++){
for(int j = 1; j <= m ; j++){
h[i][j] = h[i][j]+h[i-1][j]*P;//加上前面行的*列的乘数
}
}
// for(int i = 1; i <= n ; i++){
// for(int j = 1; j <= m ; j++)
// cout <<h[i][j]<<" ";
// cout <<endl;
// }
}
ui gethash(int x1,int x2,int y1,int y2,int mid){
ui tmp = h[x2][y2] - h[x1-1][y2]*nc[mid] - h[x2][y1-1]*mc[mid];
tmp += h[x1-1][y1-1]*nc[mid]*mc[mid];
return tmp;
}
struct node{
LL k;
int x,y;
ui data;
}book[100010];
bool check(int mid){
cnt++;
for(int i = 1; i <= n-mid+1 ; i++){
for(int j = 1; j <= m-mid+1; j++){
ui _hash = gethash(i,i+mid-1,j,j+mid-1,mid);
// if(mid == 3 && i == 1&& j == 1)
// cout <<"hash1 = "<<_hash<<endl;
// if(mid == 3&& i == 3 && j == 3)
// cout<<"hash2 = "<<_hash<<endl;
ui pos = _hash%MOD;
if(book[pos].k == cnt&& book[pos].data == _hash){
if(mid > ans){
ans = mid;
ans1x = book[pos].x;
ans1y = book[pos].y;
ans2x = i;
ans2y = j;
}
return true;
}
book[pos].k=cnt;book[pos].x = i;book[pos].y = j;book[pos].data=_hash;
}
}
return false;
}
void sov(){
int l = 1 ; int r = min(n,m);
while(l <= r){
int mid = (l+r)/2;
//cout <<"md = "<<mid<<endl;
if(check(mid)){
l = mid+1;
}
else{
r = mid-1;
}
}
if(ans1x > ans2x){
swap(ans1x,ans2x);
swap(ans1y,ans2y);
}
if(ans)
printf("%d\n%d %d\n%d %d\n",ans,ans1x,ans1y,ans2x,ans2y);
else printf("0\n");
}
int main(){
while(~scanf("%d%d",&n,&m)){
init();
sov();
}
}
vijos 有根树同构,图的hash
题意:
判断两个树同构
tip:
转换成判断两个图是否同构,对每个点的度数做hash,迭代(10层?),初始为每个点最开始的度,迭代时用上一次别的点迭代的结果(所有连边的点度数排个序)和这个点的迭代PQ不设成一个。
图的hash还可以距离啊什么
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <map>
using namespace std;
const int maxn =110;
typedef unsigned long long ui;
int a,b,n,T,tot,head[maxn],TAT[maxn],sorthead[maxn],tot2,head2[maxn];
ui d[maxn],tmp[maxn],so[maxn],ans[maxn];
map<ui,int> mp;
ui P = 1e9+7,Q = 1e9+9,X = 3214567;
struct node{
int v,next;
}edges[maxn],edges2[maxn];
void add(int u,int v){
edges[tot].v = v;edges[tot].next = head[u];head[u] = tot++;
edges[tot].v = u;edges[tot].next = head[v];head[v] = tot++;
}
void addt(int u,int v){
edges[tot].v = v;edges[tot].next = head[u];head[u] = tot++;
}
void add2(int u,int v){
edges2[tot2].v = v;edges2[tot2].next = head2[u];head2[u] = tot2++;
}
ui gethash(){
int cl = 10;
while(cl--){
for(int i = 1; i <= n ; i++){
int cnt = 0;
tmp[i] = 0;
for(int k = head[i]; k != -1; k = edges[k].next){
so[cnt++] = d[edges[k].v];
}
sort(so,so+cnt);
for(int j = 0 ; j < cnt ;j++)
tmp[i] = tmp[i]*P+so[j];
tmp[i] = tmp[i]+d[i]*Q;
}
for(int i = 1; i <= n ; i++){
d[i] = tmp[i];
}
}
sort(d+1,d+n+1);
ui no = d[1];
for(int i = 2; i <= n ; i++)
no = no*X+d[i];
return no;
}
void init(){
scanf("%d%d",&T,&n);
for(int k = 1; k <= T ; k++){
tot= 0;
for(int i = 0; i <= n; i++)
d[i] = 0,head[i] = -1;
for(int i = 1; i < n ; i++){
scanf("%d%d",&a,&b);
d[a]++;d[b]++;
add(a,b);
}
ans[k] = gethash();
//cout << ans[k]<<"we"<<endl;
}
}
void sov(){
tot = 0;
for(int i = 0 ; i <= T ;i++)
head[i] = -1;
for(int i = 1; i <= T ; i++){
if(mp[ans[i]]== 0)
mp[ans[i]] = i;
addt(mp[ans[i]],i);
//cout << mp[ans[i]]<<" -."<<i<<endl;
// cout <<"mp[ "<<ans[i]<<" ] = "<<mp[ans[i]]<<endl;
}
int cl = 0;
for(int i = 0 ; i <= T ;i++)
head2[i] = -1;
tot2 = 0;
for(int i = 1; i <= T ; i++){
if(mp[ans[i]] == i){
//cout <<" i = "<<i<<endl;
for(int p = 0 ;p <= T ;p++) TAT[p] = 0;
int num = 0;
for(int k = head[i] ; k != -1; k = edges[k].next){
//cout << "i = "<<i <<" next = "<<edges[k].v<<endl;
TAT[num++] = edges[k].v;
}
sort(TAT,TAT+num);
sorthead[cl++] = TAT[0];
for(int k = num-1; k >= 1 ; k--)
add2(TAT[0],TAT[k]);
}
}
sort(sorthead,sorthead+cl);
for(int i = 0 ; i < cl ; i++){
printf("%d",sorthead[i]);
for(int k = head2[sorthead[i]] ; k != -1; k = edges2[k].next)
printf("=%d",edges2[k].v);
printf("\n");
}
}
int main(){
init();
sov();
}
uva11996
题意:
题意:给定一个长度为n的01串,你的任务是依次执行如表所示的m条指令:
1 p c 在第p个字符后插入字符,p = 0表示在整个字符串之前插入
2 p 删除第p个字符,后面的字符往前移
3 p1 p2反转第p1到第p2个字符
4 p1 p2输出从p1开始和p2开始的两个后缀的LCP。
tip:
分块,根号(n+m)/2 ~根号(n+m)的长度,维护该块index,left块,right块,里面的字符串,字符串长度,正反hash值。
1.插入,从第一个块开始(首尾添加块0)算长度,找到属于的块,插入,若lenth>根号(n+m) ,把一个块分开,暴力后面块,前面可以直接球出来
若合格,O(lenth)的复杂度重新算hash
2.删除,若太小,则合并,合并两个块只需要O(1), 否则直接删除。。。删除时候O(lenth)
3.反转:若干整块加两边的小块,小块的地方直接暴力,求两个块新的hash值,中间部分直接反转,正反hash掉,然后left块,right块反过来O(lenth)
4.询问,二分当前lenth和,看这么长的字符串hash是否相同,O(1)求多个块合起来的hash 零碎部分暴力O(lenth*logn)
上述复杂度的lenth都是根号n的规模
以下引用论文:
如果两个串不会变化,求LCP只需要求出A+B的后缀数组即可。但是本题的A串是不断变化的,而且由变化的方式可以看出,每次操作都会导致后缀数组发生很大的变化,因此我们应该另辟蹊径。
对于一个k,不难判断两个串的LCP是否有至少k个字符:计算这两个串的前k个字符的Hash值,并且比较它们是否相等。如果相等,就几乎可以肯定地认为这两个串的LCP至少有k个字符,否则,它们的LCP长度肯定不到k。这样,就可以通过二分查找来计算LCP。
现在问题又转化成了怎么求一个子串的Hash。不妨仍然采用Rabin-Karp的Hash函数,如果已知了S1和S2的Hash值,不难求出S1+S2的Hash值:
而 的Hash值显然又是可以在O(n)时间 内预处理的得到的。因此,可以在O(1)时间内通过两个字符串的Hash值得到它们连接后得到的串的Hash值。因此,可以使用块状链表维护计算A中子串的Hash值,方法于维护计算部分和类似,不同之处在于一个字符串正向和反向的Hash值是不同的,为了能在O(n0.5)时间内完成reverse操作,应当要能在O(1)时间内把一个块“反转”,这就要求我们为一个块维护两个Hash值:一个是正向的,一个是倒向的。除此之外的操作于维护部分和或者维护最大值类似。这样,插入,删除,反转操作是O(n0.5)的,而查询操作是O(n0.5logn)的。
类似地,本题的另一种解法是在一棵splay树上维护Hash值。每次一个节点被旋转或以它为根的子树被修改时,则计算它的正向Hash值和反向Hash值,这样,就可以在O(1)时间你reverse一棵子树,通过split可以不难地把一棵子树在均摊O(logn)时间内反转。插入,删除操作显然也是均摊O(logn)的,而查询操作的均摊时间复杂度为O(log2n)
/*
题意:给定一个长度为n的01串,你的任务是依次执行如表所示的m条指令:
1 p c 在第p个字符后插入字符,p = 0表示在整个字符串之前插入
2 p 删除第p个字符,后面的字符往前移
3 p1 p2反转第p1到第p2个字符
4 p1 p2输出从p1开始和p2开始的两个后缀的LCP。
*/
/*
分块,根号(n+m)/2 ~根号(n+m)的长度,维护该块index,left块,right块,里面的字符串,字符串长度,正反hash值。
1.插入,从第一个块开始(首尾添加块0)算长度,找到属于的块,插入,若lenth>根号(n+m) ,把一个块分开,暴力后面块,前面可以直接球出来
若合格,O(lenth)的复杂度重新算hash
2.删除,若太小,则合并,合并两个块只需要O(1), 否则直接删除。。。删除时候O(lenth)
3.反转:若干整块加两边的小块,小块的地方直接暴力,求两个块新的hash值,中间部分直接反转,正反hash掉,然后left块,right块反过来O(lenth)
4.询问,二分当前lenth和,看这么长的字符串hash是否相同,O(1)求多个块合起来的hash 零碎部分暴力O(lenth*logn)
上述复杂度的lenth都是根号n的规模
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn = 3000;
const int maxm = 4e6+10;
typedef unsigned long long ui;
ui P = 3,P_power[maxn];
struct node{
int index,left,right,lenth;
int is_r;
ui f_hash,b_hash;
char s[maxn];
void roll(){
if(is_r == 1){
is_r = 0;
for(int i = 0 ,j = lenth-1; i <= j; i++,j--)
swap(s[i],s[j]);
}
}
void print(){
//roll();
if(is_r == 0)
for(int i = 0; i < lenth ; i++) cout <<s[i];
else
for(int i = lenth-1; i >= 0 ; i--) cout <<s[i];
cout <<endl;
printf("block[%d].left = %d right = % d lenth = %d f_hash = %d b_hash = %d\n",index,left,right,lenth,f_hash,b_hash);
}
ui getb_hash(){
ui tmp = 0;
for(int i = lenth-1 ;i >= 0 ; i--)
tmp = tmp *P+s[i]-'0'+1;
return tmp;
}
ui getf_hash(){
ui tmp = 0;
for(int i = 0 ;i <lenth ; i++)
tmp = tmp *P+s[i]-'0'+1;
return tmp;
}
}block[maxn];
int n,m,low_size,up_size,num_block,cp,pos,num,p1,p2,tot;
char ini[maxm],c[maxn],d[maxn];
void init(){
scanf("%s",ini);
up_size = sqrt(n+m);low_size = up_size/2;
//printf("up _ size = %d low = %d\n",up_size,low_size);
block[0].right = 1;num_block = n/low_size;
int last_size = n-num_block*low_size+low_size;
int pos = 0;
//1~倒数第二块
for(int i = 1; i < num_block ; i++){
ui tmp = 0,bk = 0;
block[i].lenth = low_size;block[i].left = i-1;block[i].right = i+1;block[i].index = i;
for(int j = 0 ;pos < n && j < block[i].lenth;j++, pos++){
block[i].s[j] = ini[pos];
tmp = tmp*P+ini[pos]-'0'+1;
}
for(int j = block[i].lenth-1; j >= 0; j--)
bk = bk*P+block[i].s[j]-'0'+1;
block[i].f_hash = tmp;block[i].b_hash = bk;
//cout <<"hahahah "<<block[i].b_hash<<endl;
block[i].is_r = 0;
}
//最后一块
block[num_block].left = num_block-1;block[num_block].right = maxn-1;block[maxn-1].left = num_block;block[maxn-1].index = maxn-1;
block[num_block].lenth = last_size;block[num_block].index = num_block;block[num_block].is_r = 0;
for(int j = 0;pos < n ;j++,pos++){
block[num_block].s[j] = ini[pos];
}
block[num_block].f_hash = block[num_block].f_hash;block[num_block].b_hash = block[num_block].b_hash;
}
void separate(int s_wi){
++num_block;
int l1 = block[s_wi].lenth/2;
int l2 = block[s_wi].lenth-l1;
block[num_block].is_r = 0;
for(int i = l1 ,j = 0; i < block[s_wi].lenth ; i++,j++) block[num_block].s[j] = block[s_wi].s[i];
block[num_block].index = num_block;
block[num_block].left = s_wi;block[num_block].right = block[s_wi].right;block[num_block].lenth = l2;
block[block[s_wi].right].left = num_block;block[s_wi].right = num_block;block[s_wi].lenth = l1;
int c1 = s_wi,c2 = num_block;
block[c1].f_hash = block[c1].getf_hash();
block[c2].f_hash = block[c2].getf_hash();
block[c1].b_hash = block[c1].getb_hash();
block[c2].b_hash = block[c2].getb_hash();
}
void cp1(int pos,int add){
if(pos == 0){
block[block[0].right].roll();
for(int i = block[block[0].right].lenth; i >= 1 ; i--)
block[block[0].right].s[i] = block[block[0].right].s[i-1];
block[block[0].right].s[0] = add+'0';
block[block[0].right].lenth++;
int c1 = block[0].right;
block[c1].f_hash = block[c1].getf_hash();
if(block[block[0].right].lenth > up_size){
separate(block[0].right);
}
return;
}
int i,cl = block[0].right;
while(pos > block[cl].lenth){
pos-=block[cl].lenth;
cl = block[cl].right;
}
block[cl].roll();
block[cl].lenth++;
for(int i = block[cl].lenth; i > pos ; i--)
block[cl].s[i] = block[cl].s[i-1];
block[cl].s[pos] = add+'0';
block[cl].f_hash = block[cl].getf_hash();
block[cl].b_hash = block[cl].getb_hash();
if(block[cl].lenth > up_size) separate(cl);
}
ui get_hash(int len,int p1){
int c1 = block[0].right;
int c3,p3 = len+p1-1;
while(p1 > block[c1].lenth){
p1 -= block[c1].lenth;
p3 -= block[c1].lenth;
c1 = block[c1].right;
}
block[c1].roll();
p1--;//c1块的第p1位置(下标)开始
c3 = c1;
while(p3 > block[c3].lenth){
p3 -= block[c3].lenth;
c3 = block[c3].right;
}
block[c3].roll();
ui tmp = 0,tp = 0;
if(c1 == c3){//同一个快
for(int i = p1 ; i < p3; i++)
tmp = tmp*P+block[c1].s[i]-'0'+1;
}
else{
for(int i = p1 ;i < block[c1].lenth; i++) tmp = tmp*P+block[c1].s[i]-'0'+1;
for(int i = c1 ; i != block[c3].left ;i = block[i].right) tmp = tmp*P_power[block[block[i].right].lenth]+block[block[i].right].f_hash;
for(int i = 0 ; i < p3; i++) tp = tp*P+block[c3].s[i]-'0'+1;
tmp = tmp*P_power[p3]+tp;
}
return tmp;
}
bool se_check(int len,int p1,int p2){
ui k1 = get_hash(len,p1);
ui k2 = get_hash(len,p2);
if(k1 == k2) return true;
else return false;
}
int cp4(int p1,int p2){
int l = 1, r = tot - p2+1,ans = 0;
while(l <= r){
int mid = (l+r)/2;
if(se_check(mid,p1,p2)){
ans = max(ans,mid);
l = mid+1;
}
else r = mid-1;
}
return ans;
}
void check(){
for(int i = block[0].right ; i != maxn-1 ; i= block[i].right){
block[i].print();
}
}
void _union(int cl){
int oth = block[cl].right;
block[block[oth].right].left = cl;
block[cl].right = block[oth].right;
for(int i = block[cl].lenth ,j = 0; j < block[oth].lenth ;i++,j++)
block[cl].s[i] = block[oth].s[j];
block[cl].lenth = block[cl].lenth+block[oth].lenth;
block[cl].f_hash = block[cl].getf_hash();
block[cl].b_hash = block[cl].getb_hash();
if(block[cl].lenth > up_size) separate(cl);
}
void cp2(int pos){
int cl = block[0].right;
while(pos > block[cl].lenth){
pos -= block[cl].lenth;
cl = block[cl].right;
}
block[cl].roll();
for(int i = pos-1 ;i < block[cl].lenth-1 ; i++){
block[cl].s[i] = block[cl].s[i+1];
}
block[cl].lenth--;
block[cl].f_hash = block[cl].getf_hash();
block[cl].b_hash = block[cl].getb_hash();
if( block[cl].lenth < low_size) {
if(block[cl].left == 0 && block[cl].right == maxn-1) return;
if(block[cl].right == maxn-1 ){
block[block[cl].left].roll();
_union(block[cl].left);
}
else {
block[block[cl].right].roll();
_union(cl);
}
}
}
void cp3(int p1,int p2){
//check();
int c1 = block[0].right;
while(p1 > block[c1].lenth){
p1 -= block[c1].lenth;
c1 = block[c1].right;
}
block[c1].roll();
//block[c1].print();
int c2 = block[0].right;
while(p2 > block[c2].lenth){
p2 -= block[c2].lenth;
c2 = block[c2].right;
}
block[c2].roll();
// block[c2].print();
if(c1 == c2){
for(int i = p1-1 ,j = p2-1; i <= j ; i++,j--){
swap(block[c1].s[i],block[c1].s[j]);
}
block[c1].f_hash = block[c1].getf_hash();
block[c1].b_hash = block[c1].getb_hash();
return;
}
for(int i = block[c1].right ; i != c2; ){
int j= block[i].right;
swap(block[i].right,block[i].left);
swap(block[i].f_hash,block[i].b_hash);
block[i].is_r ^= 1;
i = j;
// cout <"in";
}
if(block[c1].right != c2){
swap(block[c1].right , block[c2].left);
block[block[c2].left].right = c2;
block[block[c1].right].left = c1;
}
int lenc = 0,lend = 0;
for(int i = 0 ; i < p1-1;i++)
c[lenc++] = block[c1].s[i];
for(int i = p2-1 ; i >= 0 ; i--)
c[lenc++] = block[c2].s[i];
for(int i = block[c1].lenth-1; i >= p1-1 ; i--)
d[lend++] = block[c1].s[i];
for(int i = p2; i < block[c2].lenth ; i++)
d[lend++] = block[c2].s[i];
for(int i = 0 ; i < lenc ; i++){
//cout <<c[i];
block[c1].s[i] = c[i];
}
// cout <<endl;
for(int i = 0 ; i < lend ; i++){
block[c2].s[i] = d[i];
// cout <<d[i];
}
// cout <<endl;
block[c1].lenth = lenc;block[c2].lenth = lend;
block[c1].f_hash = block[c1].getf_hash();
block[c2].f_hash = block[c2].getf_hash();
block[c1].b_hash = block[c1].getb_hash();
block[c2].b_hash = block[c2].getb_hash();
// cout <<"len1 = "<<lenc <<" lenb = "<<lend<<endl;
if(lenc > up_size) separate(c1);
if(lend > up_size) separate(c2);
if(lenc < low_size){
int cl = c1;
//block[cl].print();
if(block[cl].left == 0 && block[cl].right == maxn-1) return;
if(block[cl].right == maxn-1 ){
block[block[cl].left].roll();
_union(block[cl].left);
}
else {
block[block[cl].right].roll();
_union(cl);
}
}
if(lend < low_size){
int cl = c2;
if(block[cl].left == 0 && block[cl].right == maxn-1) return;
if(block[cl].right == maxn-1 ){
block[block[cl].left].roll();
_union(block[cl].left);
}
else {
block[block[cl].right].roll();
_union(cl);
}
}
}
void sov(){
tot = n;
for(int ca = 1; ca <= m ; ca++){
scanf("%d",&cp);
if(cp == 1){
tot++;
scanf("%d%d",&pos,&num);
cp1(pos,num);
//check();
}
if(cp == 2){
tot--;
scanf("%d",&pos);
cp2(pos);
//check();
}
if(cp == 3){
scanf("%d%d",&p1,&p2);
cp3(p1,p2);
//block[2].print();
//check();
}
if(cp == 4){
scanf("%d%d",&p1,&p2);
// check();
printf("%d\n",cp4(p1,p2));
}
}
}
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
P_power[0] = 1;
for(int i = 1; i < maxn-1 ; i++)
P_power[i] = P_power[i-1]*P;
scanf("%d%d",&n,&m);
init();
sov();
}
/*
12 100
000100001100
1 0 1
4 2 4
1 1 1
4 3 4
1 1 0
4 2 8
1 3 0
4 3 5
1 7 1
4 6 9
1 8 0
4 1 8
2 8
4 6 9
2 7
4 3 5
2 3
4 2 8
2 1
4 3 4
3 2 9
3 1 3
4 1 7
3 1 2
4 1 8
*/