1. 程式人生 > 實用技巧 >#2823. 「BalticOI 2014 Day 1」三個朋友 題解

#2823. 「BalticOI 2014 Day 1」三個朋友 題解

【雜言】:

不會雜湊者,自行睡覺, 我這麼好,怎麼可能不讓你學習呢? 予以我的理解 \(link\) , 密碼是:3023227140Zmonarch . (我好吧) , 分不清 \(UNIQUE\)\(POSSIBLE\)者, 自行睡覺(呼呼呼……)


【題目描述】:

給定一個字串\(S\),先將字串\(S\)複製一次(變成雙倍快樂),得到字串\(T\),然後在\(T\)中插入一個字元,得到字串\(U\)

給出字串\(U\),重新構造出字串\(S\)。(字串\(S\)的長度 \(\leq 2e7+1\)


【思路】:

首先 , 看到構造字串, 我們就想到了字串匹配(然後你就想到了\(KMP\)

) , 然後你就想到了用雜湊幹掉這道題(請忽略我看了題目標籤,謝謝合作)

然後, 我們再次發現,對於得到的新的字串,一定不會是偶數,因為\(copy\)過後,又加了1,也就是長度應該為\(2k+1\)(其中\(k\)為字串\(S\)的長度) 。那麼就找出一個不可能的條件

再然後 ,沒有一眼結論了,那麼好, 我們換思路考慮一下 ,我們很自然的可以想到,我只需要列舉去掉一個字元之後,判斷一下,兩邊是否相等,如果相等,那麼也就必然能夠表示出來


同時我們也出現一個疑惑 , 那就是如何判斷一下形成字串\(S\)是否是唯一的呢? ,很麻煩 ,我們需要繼續分類,然後看一下是否還能分(為之後的優化打下堅實的基礎)

聯想一下雜湊, 我們發現 ,我們預處理出字首的雜湊值 , 我們還是列舉多餘字元的位置,同時,我們根據其位置與 \(\frac{n}{2}\)\(\frac{n}{2} + 1\)的關係,來判斷並求解出前後兩個字串的雜湊值(由於這是一個\(copy\),所以只需要比較第\(\frac{n}{2}\)\(\frac{n}{2}+1\)即可) 。

我們假設 \(x\)為前半部分\(hash\)值,\(y\)為後半部分\(hash\)值 ,

當我們列舉的時候,我們必然是要看一下刪除完的字串的\(Hash\)值, 那麼其中原來的\(Hash\)怎麼處理呢?(也就是剛剛我們假設的\(x\)),我們來look一下這個圖

,


當列舉到左邊的那一個字元時,

\[x = \sum\limits_{k=1} ^ {i-1} h(k) + \sum\limits_{l = i+1} ^ {\frac{n}{2}} h(l) \]

\[h(\sum\limits_{k=i+1}^{\frac{n}{2}+1}) = h(\frac{n}{2}+1) - h(i) \times power_{\frac{n}{2}+1-i} \]

同時, \(1……i-1\)\(Hash\)值為 : \(hash_{i-1}\) ,由於要和\(i+1,i+2 , …… \frac{n}{2} +1\)組成一個部分,也就是要對齊,所以,
應該乘以一個\(power_{\frac{n}{2} - i + 1}\) ,其中\(\frac{n}{2} - i +1\)表示的就是\(i+1,i+2,…… , \frac{n}{2} +1\)的長度。

列舉在左邊的處理完了,接下來看一下列舉右邊的,這時候左邊是完全不需要動的, 我們只需要動一下後面,讓他們對齊,


當列舉到左邊的那一個字元時,

\[y = hash_{i-1} - hash_{\frac{n}{2}} \times power_{i - \frac{n}{2} -1} \]

這個式子很明顯,比上一個式子簡單,且考慮的東西明顯要少。
\(i-1-\frac{n}{2}\)表示的是有半部分從開始到\(i-1\)的長度,
其開始位置為$\frac{n}{2} +1 $ ,\(n-i\)是後面的的長度,為了讓他們對齊, 所以要乘以一個\(power_{n-i}\)


然後我們可以考慮一下我們的問題了, 我們的唯一解了。 其實也是十分的簡單, 我們只需要在進行搜尋到的時候一個的時候,記錄一下第一個可以得到字串\(S\)的位置,同時在進行列舉,看一下是否還有其他的方法可以形成另一個字串\(s_{1}\),然後如果重複,就輸出應該輸出的東西就好了

終於寫完了~~~

\(code\)】:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <stack>
#define int unsigned long long
using namespace std ;
const int kmaxn = 2e7 + 10 ;
const int p1 = 1e3 + 7 ;
const int p2 = 1331 ;
const int mod1 = 10003 ;
const int mod2 = 13331 ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ;
}
int n , x , y , ans , tot , p;
bool flag ; 
char s[kmaxn] ;
int sum[kmaxn];
int power[kmaxn] ;
void prepare() //預處理 ,艹,這是板子。 
{
	power[0] = 1 ;
	for(int i = 1 ; i <= n ; i++) //預處理出p2的n次方 
	{
		power[i] = power[i - 1] * p2 ; //直接讓其無符號整數自動擷取來完成取模,省去耗時間複雜度較高的模運算 
	}
	for(int i = 1 ; i <= n ; i++) //預處理出字串的雜湊值 
	{
		sum[i] = sum[i - 1] * p2 + s[i] - 'A' + 1 ;
	}
	
}
void work()
{
	for(int i = 1 ; i <= n ; i++)
	{
		if(i <= n/2)
		{
			x = sum[n / 2 + 1] - sum[i] * power[n / 2 - i + 1] + sum[i - 1] * power[n / 2 - i + 1];
		}
		else 
		{
			x = sum[n / 2] ;
		}
		if(i <= n /2 + 1) 
		{
			y = sum[n] - sum[n / 2 + 1] * power[n / 2] ;
		}
		else 
		{
			y = (sum[i - 1] - sum[n / 2] * power[i - n / 2 - 1]) * power[n - i] + sum[n] - sum[i] * power[n - i] ;
		}
		if(x == y) 
		{
			if(flag && ans != x) //形成的字串不唯一 
			{
				printf("NOT UNIQUE\n") ;
				return ;
			}
			flag = 1 , ans = x , p = i ;
		}
	}
	if(!flag)
	{
		printf("NOT POSSIBLE\n") ;
		return ;
	}
	for(int i = 1 ; tot < n / 2 ; i ++)
	{
		if(i != p )
		{
			printf("%c" , s[i]) ;
			tot ++ ;
		}
	}
	printf("\n") ;
	return ;
}
signed main()
{
	n = read() ;
	scanf("%s" , s+1) ;
	if(n % 2 == 0) 
	{
		printf("NOT POSSIBLE\n") ;
		return 0 ;
	} //如果是偶數, 那麼刪去一個字元會形成奇數 ,但是 copy的必是偶數 , 故 不可能有解
	prepare() ; 
	work() ; 
	return 0 ; 
}