Codeforces Round 936 (Div. 2) E

HL_ZZP發表於2024-03-25

Sofia and Strings

題面翻譯

\(t\) 組資料。

每一次測試,有長度為 \(n\) 的序列 \(s\),長度為 \(m\) 的序列 \(t\)

你可以對 \(s\) 進行兩種操作:

  1. 刪除 \(s_i,1\le i\le |s|\)\(s\)\(1\) 開始標號).

  2. \(s_l,s_{l+1},\dots,s_r\) 排序(\(1\le l\le r\le|s|\))。

上面 \(|s|\)\(s\) 的長度。

判斷 \(s\) 是否可以變成 \(t\),輸出 YES 或者 NO

\(1\le t\le10^4,1\le\Sigma n,\Sigma m\le2\times10^5\)

題目描述

Sofia has a string $ s $ of length $ n $ , consisting only of lowercase English letters. She can perform operations of the following types with this string.

  1. Select an index $ 1 \le i \le |s| $ and remove the character $ s_i $ from the string.
  2. Select a pair of indices $ (l, r) $ ( $ 1 \le l \le r \le |s| $ ) and sort the substring $ s_{l} s_{l+1} \ldots s_r $ in alphabetical order.

Here, $ |s| $ denotes the current length of $ s $ . In particular, $ |s| = n $ before the first operation. For example, if $ s = \mathtt{sofia} $ , then performing the operation of the first type with $ i=4 $ results in $ s $ becoming $ \mathtt{sofa} $ , and performing the operation of the second type with $ (l, r) = (2, 4) $ after that results in $ s $ becoming $ \mathtt{safo} $ .Sofia wants to obtain the string $ t $ of length $ m $ after performing zero or more operations on string $ s $ as described above. Please determine whether it is possible or not.

輸入格式

The first line contains one integer $ t $ ( $ 1 \leq t \leq 10,000 $ ) — the number of test cases.

The first line of each test case contains two integers $ n $ , $ m $ ( $ 1\leq m \leq n \leq 2\cdot 10^5 $ ) — the lengths of string $ s $ and $ t $ , respectively.

The second line of each test case contains the string $ s $ of length $ n $ , consisting only of lowercase English letters.

The third line of each test case contains the string $ t $ of length $ m $ , consisting only of lowercase English letters.

It is guaranteed that the sum of $ n $ over all test cases does not exceed $ 2\cdot 10^5 $ .

輸出格式

For each test case, output "YES" if Sofia can obtain the string $ t $ from $ s $ using the operations above. Otherwise, output "NO".

You can output the answer in any case (upper or lower). For example, the strings "yEs", "yes", "Yes", and "YES" will be recognized as positive responses.

樣例 #1

樣例輸入 #1

8
5 5
sofia
afios
3 2
cba
bc
5 1
sofia
e
15 7
anavolimilovana
aamanan
26 4
abcdefghijklmnopqrstuvwxyz
nope
26 4
zyxwvutsrqponmlkjihgfedcba
nope
7 3
apricot
cat
3 3
cba
acb

樣例輸出 #1

YES
YES
NO
YES
NO
YES
NO
YES

提示

In the first test case, Sofia can perform the following operation:

  1. operation of the second type with $ l=1 $ and $ r=5 $ : string $ s $ becomes $ \mathtt{afios} $ after it.

In the second test case, Sofia can perform the following operations:

  1. operation of the second type with $ l=1 $ and $ r=2 $ : string $ s $ becomes $ \mathtt{bca} $ after it;
  2. operation of the first type with $ i=3 $ : string $ s $ becomes $ \mathtt{bc} $ after it.

In the third test case, it can be shown that it is impossible to obtain $ t $ from $ s $ using the provided operations.

洛谷
cf

2200的題目,我能看出來解題必要的性質,但是在使用的時候卻遇到了無從下手的問題。
其實可能還是有一個性質沒有想通,使用沒寫出來。

首先說說思考過程。

刪除操作很明顯,就是刪除多餘的字元。
很明顯,我先進行刪除操作還是先進行排序是不會影響答案的,而先刪除明顯思考起來更加方便。

而刪除並不能直接對一個字元的位置造成影響,只能間接的透過對字元的取捨達到一個類似交換位置的操作。

能對位置產生影響的是排序。而排序是不可逆的。這個部分其實有一個很不錯的思路,就是用氣泡排序的思考方式。

我們不要把排序操作當作指定一個區間的操作,而是當作只能對相鄰數字使用的操作。
可以證明,這兩個操作是完全可以等價的。證明參考氣泡排序。
那麼這個時候,問題被簡化了,有一些性質就浮出水面了。
既然我只能透過交換逆序的相鄰數字對來實現位置的交換,那些情不可能成功的情況就很好判斷了。
假如我s串現在是順序,而t串現在是逆序,這明顯就是無法成功的情況。因為這個字元沒辦法被排序到比它小的數字後去。
而如果s串是逆序,那麼不管怎麼樣,總能被交換為順序。

我的思路就卡在了這裡。
我不知道怎麼利用這個明顯是正確的思路的東西來實現。
第一個想法是先找到\(t\)中最大的字元的位置,然後找次大,這樣後面的更小的字元能夠產生的區間和需要他去的區間就被劃定了可行性也可以判斷了。
但是沒有一個可以被證明正確的貪心,也就是從這個角度這不是一個貪心能解決的問題。
就從第一個點開始,不論我怎麼取,總是沒法證明這種取法是最優的,或者是這種取法相對於其他取法是不劣的。而不是最優的意思就是有可能導致正確答案被排除。

這個時候我的思路就斷了。
從我看完題解後的角度來看,我這裡的思路其實是隨機走的。
就是我不知道為什麼我會這樣去考慮,沒有在題目裡找到一個明確的啟發性的情況下,我選擇了這種實現思路。

而在這種情況下我需要選擇什麼思路就是我這個總結需要解決的問題。
讓我們再把我們得到的需要用起來的結論列一遍。

1.對於\(t[i]<t[j]\)\(t[i]\)\(s\)中的位置的限制是\(s[i-1]\leq s[i]\),其他沒了。
2.對於\(t[i]>t[j]\)\(t[i]\)\(s\)中的位置的限制是\(s[i-1]\leq s[i] \leq s[j]\),比上面多一個限制。

其實這裡,應該有一個貪心是我需要發現的。
就是對於任何情況下,當能夠選擇的區間的一邊固定,另一邊更大總是更優秀的,反正是不劣的。
想到這個的問題在於"固定一個區間邊界",也就是想到從後往前選。也許很多人看來這個很自然就能想到,我有時候也覺得,但是這次沒有。這次我先選擇了另一種可能的貪心思路。

其實我這上面的貪心的意思就是,\(s[i]\)\(i\)越大越好。
那就很恍然大悟了。而我自己想的時候沒想到呢。這個就是"固定一個區間邊界"這個思路的應用吧,這個思路就能夠啟發出從後往前的做法。可能是其他的嗎?其實看那個需要用的結論就能看出來一點,左邊的限制都是相同的,而右邊的限制並不同。這說明了什麼?
意味著左邊更有可能因為同一種情況而變得更優秀,我們可以形象的把這比喻成左邊的"利益",也就是不管左邊是什麼情況,我是能夠採取一些操作讓左邊的利益變大的。(這邊應該說一下,上面這個對\(s[i]\)的限制同時也可以理解為是對\(s[i-1]\)的限制)
也就是如果我從左往右匹配,也就是假設左邊都取到了能讓目前他們取到答案的答案,那這對於後面的匹配是否有利,需要取決於後面的具體情況。
而如果我從右往左匹配,相當於我總是能夠估計這個操作對於之後的選擇的影響,這個變成了可量化的東西,而上面的做法找不到。
可量化也就意味著我總是能找到最優的操作。
所以這是貪心。

其實是可以說是,更喜歡固定量,而更不喜歡不確定量。

我理解的時候,把這兩種情況都不小心多理解了一些,導致我沒有意識到這個限制的特性。

這題,要是能想到從後往前做,那就直接秒了,就是純純的2200的難度。
而我上面的所有分析都是建立在我想不到的時候,要怎麼樣透過題目來找到一定的啟發性做出這個題目的情況下。

更多時候,還是提升題感,也就是看到上面的這些東西就去想想從後往前是不是更好做,事實上這個不難想到。因為限制的特性還是很明顯的。
我做這些總結的目的其實也是在於加速這種感覺的形成。不需要讓我做更多題目才有這種感覺,也讓它更加的準確吧。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
	char c=getchar();int a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
int n,m;string ss,st;
int s[200001],t[200001],last[200001][27],nxt[200001],tag[27];
bool vis[200001],ans;
int get(int x)
{
	if(vis[x]==0)return x;
	return nxt[x]=get(nxt[x]);
}
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout); 
	int T=read();
	while(T--)
	{
		for(int i=1;i<=n;i++)vis[i]=0;
		n=read(),m=read();
		cin>>ss>>st;
		for(int i=1;i<=n;i++)
			s[i]=(int)(ss[i-1]-'a'+1);
		for(int i=1;i<=m;i++)
			t[i]=(int)(st[i-1]-'a'+1);
		ans=1;
		if(m>n)
		{
			cout<<"No"<<endl;
			continue;
		}
		for(int i=1;i<=n;i++)
		{
			nxt[i]=last[i-1][s[i]];
			for(int j=1;j<=26;j++)
			{
				last[i][j]=last[i-1][j];
			}
			last[i][s[i]]=i;
		}
		for(int i=1;i<=26;i++)
		{
			tag[i]=n;
		}
		for(int i=m;i>=1;i--)
		{
			int x=last[tag[t[i]]][t[i]];
			x=get(x);
			last[tag[t[i]]][t[i]]=nxt[x];
			if(x==0)
			{
				ans=0;
				break;
			}
			vis[x]=1;
			for(int j=t[i];j<=26;j++)
			{
				tag[j]=min(tag[j],x);
			}
		}
		if(ans==0)cout<<"No"<<endl;
		else cout<<"Yes"<<endl;
	}
	return 0;
}
/*
1
5 5
sofia
afios

*/

程式碼實現又不會了...

怎麼回事。
這個把連結串列當鄰接表寫的辦法確實不錯。

相關文章