bzoj 1014 LCP 二分 Hash 匹配

求同一字符串的两个后缀的最长公共前缀。

将字符串按位置放到Splay中维护(每个节点还维护一下该子树的hash),然后二分前缀的长度,用splay计算出指定范围的hash,按hash是否相等来判断是否相同。

一开始是将字符串看成26进制,加上unsigned long long的自然溢出来计算哈希,但这样Wa掉了,改成27进制就AC了,但我还不知道为什么,望明者相告,谢。

  1 /**************************************************************
  2     Problem: 1014
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:7188 ms
  7     Memory:4900 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #include <iostream>
 12 #include <cstring>
 13 #define maxn 100030
 14 using namespace std;
 15
 16 typedef unsigned long long ulng;
 17
 18 struct Splay {
 19     int pre[maxn], son[maxn][2], ch[maxn], siz[maxn], ntot, root;
 20     ulng hash[maxn];
 21     ulng power[maxn];
 22
 23     int newnode( int p, int c ) {
 24         int nd = ++ntot;
 25         pre[nd] = p;
 26         son[nd][0] = son[nd][1] = 0;
 27         ch[nd] = c;
 28         siz[nd] = 1;
 29         hash[nd] = c;
 30         return nd;
 31     }
 32     void update( int nd ) {
 33         int ls = son[nd][0], rs = son[nd][1];
 34         siz[nd] = siz[ls]+siz[rs]+1;
 35         hash[nd] = hash[ls]*power[siz[rs]+1]+ch[nd]*power[siz[rs]]+hash[rs];
 36     }
 37     int build( int p, const char *str, int lf, int rg ) {   //  [lf,rg]
 38         if( lf>rg ) return 0;
 39         if( lf==rg ) return newnode(p,str[lf]-‘a‘);
 40         int mid = (lf+rg)>>1;
 41         int nd = newnode( p, str[mid]-‘a‘ );
 42         son[nd][0] = build( nd, str, lf, mid-1 );
 43         son[nd][1] = build( nd, str, mid+1, rg );
 44         update( nd );
 45         return nd;
 46     }
 47     void init( const char *str ) {
 48         root = ntot = 0;
 49         power[0] = 1ULL;
 50         for( int i=1; i<maxn; i++ )
 51             power[i] = power[i-1]*27ULL;
 52
 53         root = newnode( 0, ‘a‘-‘a‘ );
 54         int rnd = son[root][1] = newnode( root, ‘a‘-‘a‘ );
 55         son[rnd][0] = build( rnd, str, 0, strlen(str)-1 );
 56         update( rnd );
 57         update( root );
 58     }
 59     void rotate( int nd, int d ) {
 60         int p = pre[nd];
 61         int s = son[nd][!d];
 62         int ss = son[s][d];
 63
 64         son[nd][!d] = ss;
 65         son[s][d] = nd;
 66         if( p ) son[p][ nd==son[p][1] ] = s;
 67         else root = s;
 68
 69         pre[s] = p;
 70         pre[nd] = s;
 71         if( ss ) pre[ss] = nd;
 72
 73         update( nd );
 74         update( s );
 75     }
 76     void splay( int nd, int top=0 ) {
 77         while( pre[nd]!=top ) {
 78             int p = pre[nd];
 79             int nl = nd==son[p][0];
 80             if( pre[p]==top ) {
 81                 rotate( p, nl );
 82             } else {
 83                 int pp = pre[p];
 84                 int pl = p==son[pp][0];
 85                 if( nl==pl ) {
 86                     rotate( pp, pl );
 87                     rotate( p, nl );
 88                 } else {
 89                     rotate( p, nl );
 90                     rotate( pp, pl );
 91                 }
 92             }
 93         }
 94     }
 95     int find( int pos ) {
 96         int nd = root;
 97         while(1) {
 98             int ls = siz[son[nd][0]];
 99             if( nd==0 ) while(1);
100
101             if( pos<=ls ) {
102                 nd = son[nd][0];
103             } else if( pos>=ls+2 ) {
104                 nd = son[nd][1];
105                 pos -= ls+1;
106             } else return nd;
107         }
108     }
109     void add_ch( int pos, int c ) {
110         int lnd = find(pos);
111         int rnd = find(pos+1);
112         splay(lnd);
113         splay(rnd,lnd);
114         son[rnd][0] = newnode( rnd, c );
115         splay( son[rnd][0] );
116     }
117     void chg_ch( int pos, int c ) {
118         int nd = find(pos);
119         ch[nd] = c;
120         update( nd );
121         splay( nd );
122     }
123     ulng qu_hash( int lf, int rg ) {
124         int lnd = find(lf-1);
125         int rnd = find(rg+1);
126         splay( lnd );
127         splay( rnd, lnd );
128         return hash[son[rnd][0]];
129     }
130     inline int size() { return siz[root]-2; }
131 };
132
133 Splay T;
134 char str[maxn];
135 int m;
136
137 int main() {
138     scanf( "%s", str );
139     T.init(str);
140     scanf( "%d", &m );
141     while(m--) {
142         char ch[10];
143         int pos, sa, sb;
144         scanf( "%s", ch );
145         if( ch[0]==‘I‘ ) {
146             scanf( "%d%s", &pos, ch );
147             pos++;
148             T.add_ch( pos, ch[0]-‘a‘ );
149         } else if( ch[0]==‘R‘ ) {
150             scanf( "%d%s", &pos, ch );
151             pos++;
152             T.chg_ch( pos, ch[0]-‘a‘ );
153         } else {
154             scanf( "%d%d", &sa, &sb );
155             sa++, sb++;
156             if( T.qu_hash(sa,sa) != T.qu_hash(sb,sb) ) {
157                 printf( "0\n" );
158                 continue;
159             }
160             int len = T.size() - max(sa-1,sb-1) + 1;
161             int rl = 1, rr = len;
162             while( rl<rr ) {
163                 int md = (rl+rr+1)>>1;
164                 ulng ha = T.qu_hash( sa, sa+md-1 );
165                 ulng hb = T.qu_hash( sb, sb+md-1 );
166                 if( ha==hb ) rl = md;
167                 else rr = md-1;
168             }
169             printf( "%d\n", rl );
170         }
171     }
172 }
173
174 

时间: 2024-10-26 07:02:48

bzoj 1014 LCP 二分 Hash 匹配的相关文章

bzoj 1014 splay维护hash值

被后缀三人组虐了一下午,写道水题愉悦身心. 题很裸,求lcq时二分下答案就行了,写的不优美会被卡时. (写题时精神恍惚,不知不觉写了快两百行...竟然调都没调就A了...我还是继续看后缀自动机吧...) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define bas 131 6 #define p 1000000007 7 #def

[BZOJ 1014] [JSOI2008] 火星人prefix 【Splay + Hash】

题目链接:BZOJ - 1014 题目分析 求两个串的 LCP ,一种常见的方法就是 二分+Hash,对于一个二分的长度 l,如果两个串的长度为 l 的前缀的Hash相等,就认为他们相等. 这里有修改字符和插入字符的操作,所以用 Splay 来维护串的 Hash 值. 一个节点的值就是它的子树表示的字串的 Hash 值. 使用 unsigned long long 然后自然溢出就不需要 mod 了,速度会快很多. 代码 #include <iostream> #include <cstd

BZOJ 1014: [JSOI2008]火星人prefix( splay + hash )

用splay维护序列, 二分+hash来判断LCQ.. #include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int maxn = 100009; const int P = 1000173169; ull K[maxn]; int N; char S[maxn]; struct Node { Node *ch[2], *p; int s, v; ull h; inline void

【二分+hash】【字符串】【平衡树】【JSOI 2008】火星人prefix

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec Memory Limit: 162 MB Submit: 4264 Solved: 1306 Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人定义了一个函数LCQ(x, y)

BZOJ 3790 神奇项链 hash/后缀自动机+贪心

Description 母亲节就要到了,小 H 准备送给她一个特殊的项链.这个项链可以看作一个用小写字母组成的字符串,每个小写字母表示一种颜色. 为了制作这个项链,小 H 购买了两个机器.第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠.例如:aba和aca连接起来,可以生成串abaaca或 abaca. 现在给出目标项链的样式,询问你需要使用第二个机器多少次才

poj 1743 最长不重叠重复子串 后缀数组+lcp+二分

题比较容易读懂,但是建模需动点脑子: 一个子串加常数形成的子串认为跟子串相同,求最长不重叠重复子串 题目中说 is disjoint from (i.e., non-overlapping with) at least one of its other appearance(s) 意味着不能重叠,举个例子 1, 2,3,  52, 53,54 1,2, 3和 52, 53,54满足题意,差值为51 枚举差值肯定不行------看了题解明白的:: 后项减去前一项得到: 1,1,1,49,1,1  

【BZOJ1414/3705】[ZJOI2009]对称的正方形 二分+hash

[BZOJ1414/3705][ZJOI2009]对称的正方形 Description Orez很喜欢搜集一些神秘的数据,并经常把它们排成一个矩阵进行研究.最近,Orez又得到了一些数据,并已经把它们排成了一个n行m列的矩阵.通过观察,Orez发现这些数据蕴涵了一个奇特的数,就是矩阵中上下对称且左右对称的正方形子矩阵的个数. Orez自然很想知道这个数是多少,可是矩阵太大,无法去数.只能请你编个程序来计算出这个数. Input 文件的第一行为两个整数n和m.接下来n行每行包含m个正整数,表示Or

UVA - 12338 Anti-Rhyme Pairs 二分+hash

题目链接:https://vjudge.net/problem/UVA-12338 题意: 给你n个串 问你任意两个串的最大公共前缀长度是多少 题解: 二分+hash 思路很明显,我最近用来写hash 很鸡肋 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define ls i<<1 #define rs ls

BZOJ 1014 火星人 | 平衡树维护哈希

BZOJ 1014 火星人 题意 有一个字符串,三中操作:在某位置后面插入一个字符.修改某位置的字符.询问两个后缀的最长公共前缀. 题解 看到网上的dalao们都说这道题是平衡树,我就很懵x--平衡树维护什么啊? 最后发现某个节点维护的是它所代表的区间的哈希值--显然这个哈希值可以从左右子树的哈希值和这个节点上的字符算出来. #include <cstdio> #include <cmath> #include <cstring> #include <algori