zoukankan      html  css  js  c++  java
  • 【牛客网】白兔的字符串(扩展kmp+hash+最小表示法)

    【牛客网】15253 白兔的字符串

    比较两个字符串是否循环同构从比较两个最小表示是否相同开始

    我们把T转成循环同构,这不影响答案

    然后我们要假定T真的是(S)长度为(len(T))区间的一个最小表示,对于这个区间设为([l,r]),我们设另一个在(S)上的指针为(p),如果(p)匹配到了区间的末尾,那可以认为(p)开头的区间的一个循环同构表示法“可能是最小的”(任意一个后缀([p',r])不会大于([p,r])长度相等的前缀部分,因为([p,r])匹配了(T)的一个前缀,(T)转化成了最小表示),这个时候只要用([l,p - 1])(T)的相应长度的后缀比较一下是否相等即可

    这里考虑以下为什么当([p,r])可以匹配前缀,存在(p' > p)([p',r])也可以匹配前缀,为什么不会出现错误

    因为我们假定了这个区间是循环同构(T)的,而只要它等于T,则(p)必然是区间循环同构的开头,而(p')也可能是循环同构的开头,所以找谁不重要

    而不等于(T),这个性质也没有什么作用,(p)是不是最小表示法的开头都无所谓

    如果发现(p)开始的循环表示是比(T)严格大的一个串,类似与最小表示法的算法这一整段区间都可以跳过

    如果是比(T)严格小呢?这个区间肯定是废了(不可能统计进答案了),但是为了编码的统一性,让(p)继续右移找到后缀匹配(T)的前缀可以保证操作的复杂度正确。为了复杂度正确,如果匹配了(k)长度的串,让(p)直接跳到(p + k)似乎不错,复杂度对了,但是这显然有问题

    那么这既然是([p,p + k - 1])(T)的一段前缀,扩展kmp正好可以处理所有位置开始的后缀和前缀匹配长度,所以先对T跑一遍扩展kmp,然后对([p,p + k - 1])的每一个位置判断能否满足([p',p + k - 1])也匹配(T)的开头,让过程继续下去

    如果(p = l),下一个区间是([l + 1,r + 1]),需要用类似的操作右移一下(p)

    比较两个字符串是否相同可以哈希,防止冲突可以来双哈希

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define mo 99994711
    //#define ivorysi
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define pii pair<int,int>
    typedef long long int64;
    using namespace std;
    
    template<class T>
    void read(T &res) {
    	res = 0;T f = 1;char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-') f = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9') {
    		res = res * 10 + c - '0';
    		c = getchar();
    	}
    	res *= f;
    }
    template<class T>
    void out(T x) {
    	if(x < 0) {x = -x;putchar('-');}
    	if(x >= 10) out(x / 10);
    	putchar('0' + x % 10);
    }
    int mod[2] = {99994711,99977797};
    int ba[2] = {91,47};
    int e[2][2000006];
    struct Hash {
    	int hsh[2][2000006];
    
    	void Init(char *t,int lenT) {
    		for(int i = 1 ; i <= lenT ; ++i) {
    			for(int j = 0 ; j <= 1 ; ++j) {
    				hsh[j][i] = 1LL * e[j][i] * (t[i] - 'a' + 1) % mod[j];
    				hsh[j][i] = hsh[j][i] + hsh[j][i - 1];
    				if(hsh[j][i] >= mod[j]) hsh[j][i] -= mod[j];
    			}
    		}
    	}
    
    	pair<int,int> Query(int l,int r) {
    		int res[2] = {0,0};
    		for(int j = 0 ; j <= 1; ++j) {
    			res[j] = hsh[j][r] - hsh[j][l - 1] + mod[j];
    			if(res[j] >= mod[j]) res[j] -= mod[j];
    			res[j] = 1LL * res[j] * e[j][1000000 - l] % mod[j];
    		}
    		return make_pair(res[0],res[1]);
    	}
    }hsh[2];
    char T[2000006],S[2000006];
    int N,lenT,lenS,exd[2000005];
    void cycle(char *t,int st,int len) {
    	static char tmp[1000006];
    	int cnt = 0;
    	for(int i = st ; i <= len ; ++i) tmp[++cnt] = t[i];
    	for(int i = 1 ; i < st ; ++i) tmp[++cnt] = t[i];
    	for(int i = 1 ; i <= len ; ++i) t[i] = tmp[i];
    }
    bool check(int l1,int r1,int l2,int r2) {
    	if(r1 < l1) return true;
    	pair<int,int> a,b;
    	a = hsh[1].Query(l1,r1);b = hsh[0].Query(l2,r2);
    	return a == b;
    }
    void Init() {
    	scanf("%s",T + 1);
    	lenT = strlen(T + 1);
    	for(int i = 1 ; i <= lenT ; ++i) T[i + lenT] = T[i];
    	int p = 1,q = 2,k = 1;
    
    	while(q <= lenT) {
    		if(T[p + k - 1] == T[q + k - 1]) ++k;
    		else if(T[p + k - 1] > T[q + k - 1]) {p = max(p + k,q + 1);k = 1;}
    		else if(T[p + k - 1] < T[q + k - 1]) {q = q + k;k = 1;}
    		if(p > q) swap(p,q);
    	}
    	cycle(T,p,lenT);
    	e[0][0] = e[1][0] = 1;
    	for(int i = 1 ; i <= 2000000 ; ++i) {
    		for(int j = 0 ; j <= 1 ; ++j) {
    			e[j][i] = 1LL * e[j][i - 1] * ba[j] % mod[j];
    		}
    	}
    
    	hsh[0].Init(T,lenT);
    
    	int maxpos = 0;
    	for(int i = 2 ; i <= lenT ; ++i) {
    		if(maxpos) exd[i] = min(exd[i - maxpos + 1],exd[maxpos] + maxpos - i);
    		while(i + exd[i] <= lenT && T[i + exd[i]] == T[exd[i] + 1]) ++exd[i];
    		if(exd[i] && i + exd[i] - 1 > maxpos + exd[maxpos] - 1) maxpos = i;
    	}
    }
    int main() {
    #ifdef ivorysi
    	freopen("f1.in","r",stdin);
    #endif
    	Init();
    
    	read(N);
    
    	for(int i = 1 ; i <= N ; ++i) {
    		scanf("%s",S + 1);
    		lenS = strlen(S + 1);
    		hsh[1].Init(S,lenS);
    		int p = 1,k = 0 ;
    		int ans = 0;
    		for(int j = 1 ; j <= lenS - lenT + 1; ++j) {
    			while(p + k  <= j + lenT - 1) {
    				if(T[k + 1] == S[p + k]) ++k;
    				else if(T[k + 1] < S[p + k]) {p = p + k + 1;k = 0;}
    				else if(T[k + 1] > S[p + k]) {
    					bool f = 0;
    					for(int h = 2 ; h <= k ; ++h) {
    						if(h + exd[h] - 1 >= k) {
    							p = p + h - 1;
    							k = k - h + 1;
    							f = 1;
    							break;
    						}
    					}
    					if(!f) {
    						if(k != 0) {p = p + k;k = 0;}
    						else {++p; k = 0;}
    					}
    				}
    			}
    			//printf("j:%d p:%d k:%d
    ",j,p,k);
    			if(check(j,p - 1,k + 1,lenT)) {
    				++ans;
    				//printf("j:%d
    ",j);
    			}
    
    			if(p == j) {
    				for(int h = 2 ; h <= lenT ; ++h) {
    					if(h + exd[h] - 1 >= lenT) {
    						p = p + h - 1;
    						k = j + lenT - p;
    						break;
    					}
    				}
    				if(p == j) {p = j + lenT;k = 0;}
    			}
    		}
    		out(ans);enter;
    	}
    }
    
  • 相关阅读:
    MVVMLight leaning note
    c# Random Class usage
    Learning note for Binding and validation
    Content Template & DataTemplate 区别
    ListBox mvvm 学习笔记
    spinlock自旋锁de使用
    linux 内核(驱动)常用函数
    linux 编译,链接和加载
    Linux 下多核CPU知识
    linux 内核调试
  • 原文地址:https://www.cnblogs.com/ivorysi/p/14361316.html
Copyright © 2011-2022 走看看