Official

F - Avoid K Palindrome 2 Editorial by mechanicalpenciI


\(S\)の文字を並び替えて得られる文字列を全列挙し、それぞれについて長さ \(K\) の回文を部分文字列として含むかを判定すれば良いです。

\(S\)の文字を並び替えて得られる文字列の列挙は、c++であれば next_permutation で行うことができます。pythonでもitertools.permutations で列挙することができますが、こちらは元の列において異なる位置に存在する同一文字を区別するので、適切に重複する文字列を取り除く必要があります。その他、標準関数が定義されていない言語においては自身でそのような関数を実装する必要があります。順列を列挙するアルゴリムはいくつか知られており、検索等によっても見つけることができますが、有名なものの \(1\) つは次のようなものです。

\(P=(P_1,P_2,\ldots,P_N)\) を並べ替えて得られる列であって、辞書順で\(P\) の次の順列は次のようにして得られます。

  1. \(P_i<P_{i+1}\) をみたす最大の \(i\) \((1\leq i\leq N-1)\) をとる.
  2. 1. の \(i\) に対して、\(P_i<P_j\) となる最大の \(j\) をとる。
  3. \(P_i\)\(P_j\) をスワップする.
  4. \(P_{i+1},P_{i+2},\ldots,P_N\) を逆順に並べ替える。

最初昇順にソートした列に対して、(降順にソートした列になるまで)この操作を繰り返すことによって、並べ替えの列をすべて列挙することができます。

これらのアルゴリズムでは \(O(N!)\) ですべての列を列挙することができます。よって、全体の計算量は \(O(N!\cdot(NK))\) となりますが、\(10!\sim 3\times 10^6\) であるため、今回の制約下で十分高速です。 よって、この問題を解くことができました。

c++ による実装例:

#include <bits/stdc++.h>
using namespace std;

int main() {
	int n,k;
	string s;
	cin>>n>>k;
	cin>>s;

	vector<int>a;
	for(int i=0;i<n;i++)a.push_back(((int)(s[i]-'a')));
	sort(a.begin(),a.end());
	int ans=0;
	bool ok,flag;

	while(true){
		ok=true;
		for(int i=0;i<=n-k;i++){
			flag=true;
			for(int j=0;j<k;j++){
				if(a[i+j]!=a[i+k-1-j])flag=false;
			}
			if(flag)ok=false;
		}
		if(ok)ans++;
		if(!next_permutation(a.begin(), a.end()))break;
	}
	
	cout<<ans<<endl;
	return 0;
}

posted:
last update: