P6739 [BalticOI 2014 Day1] Three Friends 題解

寫在前面

P6739 [BalticOI 2014 Day1] Three Friendsios

據說這題能夠用比較暴力的作法過,好比 \(string\) 裏面自帶的 \(substr\) ,能夠看這位大佬的提交記錄git

模數不要用 \(49999\) ,會被卡, \(1e9+9\) 纔是真愛數組

Solution

何爲字符串哈希(可跳過):

因爲字符串是具備先後關係的,能夠按下述方法構造:
選取兩個合適的互質常數 \(b\)\(h (b < h)\), 假設有一個字符串 \(C = c_1c_2···c_m\),那麼咱們定義哈希函數:函數

\[H(C) = (c_1b^{m - 1} + c_2b^{m - 2} + ···+c_mb^{0}) \mod h \]

考慮遞推實現,設 \(H(C, k)\) 爲前 \(k\) 個字符構成的字符串的哈希值,則:spa

\[H(C, k + 1) = H(C, k) \times b + c_{k + 1} \]

一般,題目要求的是判斷主串的一段字符與另外一個匹配串是否匹配,即判斷字符串 \(C = c_1c_2···c_m\) 從位置 \(k + 1\) 開始的長度爲 \(n\) 的子串 \(C^{'} = c_{k + 1}c_{k + 2}···c_{k + n}\) 的哈希值與另外一匹配串 \(S = s_1s_2···s_n\) 的哈希值是否相等,則:code

\[H(C_{'}) = H(C, k + n) - H(C, k) \times b^{n} \]

只要預求得 \(b^{n}\) ,就能 \(O(1)\) 判斷了ci

能夠預處理出全部 \(b^{n}\) 存在 \(Pow\) 數組裏字符串


觀察目標串 \(U\) 的構造方式,發現若是 \(N\) 是偶數,必定沒法構造get

而後考慮枚舉刪除每個字符,再將剩下的字符串均分判斷哈希值是否相等string

假設刪去的字符在前半段,那麼後半段的必定是原字符串 \(S\),若是在後半段,那麼前半段必定是原字符串 \(S\)

因此能夠分開枚舉,而且預處理出對應的原字符串

那麼刪掉一個字符後,剩下的兩段怎麼合併呢?以樣例字符串爲例:

\[ABXCABC \]

假設枚舉到 \(X\)

那麼原字符串爲 \(ABC\),前面要合併的兩段字符串是 \(AB\)\(C\)

若是樸素計算這兩個串的哈希值及原字符串哈希值,計算過程以下:

\[AB = A * b^{1} + B * b^{0} \]

\[C = C * b^{0} \]

\[ABC = A * b^{2} + B * b^{1} + C * b^{0} \]

因此不難看出 \(ABC = AB * b^{1} + C\)

對這個結論進行推廣,對於字符串 \(X\),刪掉其中一個字符後,分紅兩個字符串\(X_1,X_2\),有

\[X = X_1 * b^{len_{X_2}} + X_2 \]

根據這個公式去進行合併,而後比較兩個字符串哈希值是否相同

而後這就能夠了嗎? 不不不

看這個樣例

13
AABCABCABCABC

咱們會發現刪掉第一個和第二個字符均可以,但獲得的原串都是 \(ABCABC\)

因此注意開個map判重便可

若是還A不了,兄弟,改個模數試試吧

Code

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 2e6+10;
const int INF = 1;
const int mod = 1e9+9;
const int b = 7;

int len, cnt = 0;
char s[MAXN];
LL Pow[MAXN], Pow2, sum, H[MAXN], wz;
map<LL, LL> Map;

int read(){
	int s = 0, f = 0;
	char ch = getchar();
	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
	return f ? -s : s;
}

int main()
{
	len = read();
	if(len % 2 == 0) {
		printf("NOT POSSIBLE");
		return 0; 
	} 
	cin>>(s + 1);
//	for(int i = 1; i <= len; ++i) {
//		cout<<s[i]<<" ";
//	}
//	cout<<"\n";
	Pow[0] = 1;
	for(int i = 1; i <= len; ++i){
		Pow[i] = Pow[i - 1] * b % mod;
		H[i] = (H[i - 1] * b % mod + s[i]) % mod;
	}
	sum = (H[len] - H[len / 2 + 1] * Pow[len / 2] % mod + mod) % mod; //後半段的哈希值 
	for(int i = 1; i <= len / 2 + 1; ++i){
		LL pre = H[i - 1] * Pow[len / 2 + 1 - i] % mod;//刪掉枚舉字符後的剩餘字符串的哈希值 
		if(Map[sum] == 0 && sum == (H[len / 2 + 1] - H[i] * Pow[len / 2 + 1 - i] % mod + pre + mod) % mod){//算出後面那一段並進行拼接 
			cnt++;
			Map[sum]++;
			wz = i;
		}
	}
//	cout<<cnt<<"lkp\n";
	sum = H[len / 2];//前半段的哈希值 
	for(int i = len / 2 + 2; i <= len; ++i){
		LL pre = (H[i - 1] - H[len / 2] * Pow[i - len / 2 - 1] % mod + mod) % mod * Pow[len - i] % mod;//刪掉枚舉字符後的剩餘字符串的哈希值 
		if(Map[sum] == 0 && sum == (H[len] - H[i] * Pow[len - i] % mod + pre + mod) % mod){//算出後面那一段並進行拼接 
			cnt++;
			Map[sum]++;
			wz = i;
		}
	} 
//	cout<<cnt<<"zsf\n";
	if(cnt == 0) printf("NOT POSSIBLE");
	else if(cnt == 1) {
		if(wz <= len / 2) for(int i = len / 2 + 2; i <= len; ++i) cout<<s[i];
		else for(int i = 1; i <= len / 2; ++i) cout<<s[i];
	}
	else printf("NOT UNIQUE"); 
	return 0;
}
相關文章
相關標籤/搜索