Official

E - One Time Swap Editorial by mechanicalpenciI


以下では、\(S\) の長さを \(N\) とします。また、 \(S\)\(i\) 文字目\((1\leq i\leq N)\)\(S_i\) で表します。
さらに、\(1\leq i<j\leq N\) をみたす整数の組 \((i,j)\) について、\(S\)\(i\) 文字目と \(j\) 文字目を入れ替えた文字列を \(S(i,j)\) と書くことにします。

\((i,j)\neq (i',j')\) かつ \(S(i,j)\)\(S(i',j')\) が同じとなる条件について考えます。\(S_i\neq S_j\) とすると、\(S\)\(S(i,j)\)\(i\) 文字目と \(j\) 文字目だけが異なります。操作後の文字列であって、そのようなものは \(S(i,j)\) しかありません。よって、\(S_i\neq S_j\) のとき他に \(S(i,j)\)\(S(i',j')\) が同じとなるような \((i',j')\) は他に存在しません。
一方、\(S_i=S_j\) とすると、\(S(i,j)\)\(S\) と全く同じ文字列になります。よって、\(S_{i'}=S_{j'}\) であるような \(i',j'\) についての \(S(i',j')\) と一致します。

これを整理すると、次のことがわかります。

  • 操作後の文字列であって \(S\) と異なるものは、\(1\leq i<j\leq N\) かつ \(S_i\neq S_j\) であるような \((i,j)\) と一対一に対応し、すなわちそのような組の数だけ存在する。
  • 操作後の文字列として \(S\) そのものがあり得るかは、\(S\) の中にある文字が \(2\) 回以上存在するかによって決まる。すなわち \(S\) の文字列がすべて異なるならばあり得ず、そうでないならばあり得る。

後者の判定は \(S\) の文字を \(1\) つずつ見ていくだけで良いです。(なお、\(S\) の長さが \(27\) 以上であれば調べずともかならず存在します。)

前者については、各 \(i,j\) の組に対して計算しようとすると \(O(N^2)\) の計算量がかかり、時間制限に間に合わせるのが難しいです。しかし、\(S\) にそれぞれの文字が何個含まれているか確認することによっても求めることができます。
具体的には、\(S\)a, b, \(\ldots\), z がそれぞれ \(C_1, C_2,\ldots, C_{26}\) 個含まれているとすると、\(C_1C_2+C_1C_3+\cdots+C_1C_{26}+C_2C_3+\cdots+C_2C_{26}+C_3C_4+\cdots+C_{25}C_{26}\) 個であるのでこれを直接計算しても良いですが、\(S_i\)\(S_j\) が同じ文字であるような組を差し引いて、\(\frac{1}{2}\{(C_1+C_2+\cdots+C_{26})^2-C_1^2-C_2^2\cdots-C_{26}^2\}=\frac{1}{2}(N^2-C_1^2-C_2^2\cdots-C_{26}^2)\) として計算しても良いです。

いずれの方法でも \(O(N)\) でこの問題を解くことができ、十分に高速です。
よってこの問題を解くことができました。

c++ による実装例:

#include <bits/stdc++.h>

using namespace std;

int main() {
	long long n,ans=0;
	vector<long long>cnt(26);
	bool same=false;
	string s;

	cin>>s;
	n=s.size();
	for(int i=0;i<n;i++){
		cnt[((int)(s[i]-'a'))]++;
	}
	ans=n*n;
	for(int i=0;i<26;i++){
		ans-=cnt[i]*cnt[i];
		if(cnt[i]>1)same=true;
	}
	ans/=2;
	if(same)ans++;
	cout<<ans<<endl;

	return 0;
}

posted:
last update: