E - Typo Pairs Editorial
by
mechanicalpenciI
包除原理を用いてこの問題を解くことを考えます。
整数の組 \((i,j)\) \((1\leq i<j\leq N)\) であって、\(S_i\) と \(S_j\) が等しいもの、すなわちハミング距離が \(0\) のものの集合を \(A\) とします。
次に、\(1\leq k\leq M\) について、整数の組 \((i,j)\) \((1\leq i<j\leq N)\) であって、\(S_i\) と \(S_j\) が \(k\) 文字目以外すべて等しいもの(\(k\) 文字目は等しくても等しくなくても良い)集合を \(B_k\) とします。
最後に、\(1\leq k<k'\leq M\) について、整数の組 \((i,j)\) \((1\leq i<j\leq N)\) であって、\(S_i\) と \(S_j\) が \(k,k'\) 文字目以外すべて等しいもの(\(k,k'\) 文字目は等しくても等しくなくても良い)集合を \(C_{k,k'}\) とします。
さて、それぞれの \((i,j)\) \((1\leq i\leq j)\) が\(A,B_k,C_{k,k'}\) のうちのいくつに含まれるか考えます。
- \(S_i\) と \(S_j\) が完全に等しいとき
\((i,j)\) はすべての集合に含まれます。すなわち、\(A\)、\(B_1,B_2,\ldots,B_M\) のうち \(M\) 個、\(C_{1,2},\ldots,C_{M-1,M}\) のうち \(M(M-1)/2\) 個に含まれます。
- \(S_i\) と \(S_j\) が \(k\) 文字目のみ異なるとき
\((i,j)\) は \(A\) に含まれません。\((i,j)\) は \(B_1,B_2,\ldots,B_M\) のうち \(B_k\) のみに含まれます。\(C_{1,2},\ldots,C_{M-1,M}\) のうち 、\(C{1,k},\ldots, C{k-1,k}, C_{k,k+1},\ldots,C_{k,M}\) の\(M-1\) 個に含まれます。
- \(S_i\) と \(S_j\) が \(k,k'\) 文字目 \((k<k')\) のみ異なるとき
\((i,j)\) は \(A\) および \(B_1,B_2,\ldots,B_M\) に含まれません。また、\(C_{1,2},\ldots,C_{M-1,M}\) のうち 、\(C_{k,k'}\) にのみ含まれます。
- \(S_i\) と \(S_j\) が \(3\) 文字以上異なるとき
\((i,j)\) は \(A\)、\(B_1,B_2,\ldots,B_M\)、\(C_{1,2},\ldots,C_{M-1,M}\) のいずれにも含まれません。
さて、
\[ X=\frac{(M-1)(M-2)}{2}\lvert A\rvert-(M-2)\displaystyle\sum_{k=1}^M\lvert B_k\rvert+\displaystyle\sum_{k=1}^{M-1}\displaystyle\sum_{k'=k+1}^{M}\lvert C_{k,k'}\rvert\qquad \qquad (1) \]
という量を考えます。 ここで、集合 \(T\) に対して \(\lvert T\rvert\) で \(T\) の要素数を表します。 このとき、各 \((i,j)\) が何回数えられているか考えると、
- \(S_i\) と \(S_j\) が完全に等しいとき: \(\frac{(M-1)(M-2)}{2}\times 1-(M-2)\times M+1\times \frac{M(M-1)}{2}=1\)
- \(S_i\) と \(S_j\) が\(1\)文字のみ異なるとき: \(\frac{(M-1)(M-2)}{2}\times 0-(M-2)\times 1+1\times (M-1)=1\)
- \(S_i\) と \(S_j\) が\(2\)文字のみ異なるとき: \(\frac{(M-1)(M-2)}{2}\times 0-(M-2)\times 0+1\times1=1\)
- \(S_i\) と \(S_j\) が\(3\)文字以上異なるとき: \(\frac{(M-1)(M-2)}{2}\times 0-(M-2)\times 0+1\times 0=0\)
となるため、これは求めるべき答えと一致します。
\(\lvert A\rvert\) を求めるには、\(S_1,\ldots,S_N\) の中に各文字列が何個ずつ存在するかを数えれば良いです。すなわち、同じ文字列がそれぞれ \(x_1,x_2,\ldots, x_p\) 個ある \((1\leq x_i\), \(x_1+x_2+\cdots+x_p=N)\) とき、$\(\lvert A\rvert=\frac{x_1(x_1-1)}{2}+\frac{x_2(x_2-1)}{2}+\cdots+\frac{x_p(x_p-1)}{2}\) で求められます。
これは unorederd_map (C++) や dict (Python) で管理したり、\(S_1,S_2,\ldots, S_N\) をソートして尺取り法で数えるなどすることで求められます。
\(B_k, C_{k,k'}\) についても、\(k\) 文字目のみ、あるいは \(k,k'\) 文字目のみを除いた文字列について同様のことを行えば良いです。
計算量は実装によって異なりますが、\(O(NM^3)\) または \(O(NM^2(M+\log N))\)などになります。
よって、この問題を解くことができました。
なお、\((1)\) 式の右辺の係数については、各 \((i,j)\) が \(A, B_k,C_{k,k'}\) にいくつずつ含まれているかという情報から、ちょうど \(1\) 回ずつカウントされるように逆算することが可能です。
c++による実装例;
#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for(ll i = 0; i < n; ++i)
int main(void){
long long n,m;
vector<long long>a;
long long x,y,z,ans=0;
cin>>n>>m;
vector<string>s(n);
for(int i=0;i<n;i++)cin>>s[i];
for(int i=0;i<m;i++){
for(int j=0;j<=i;j++){
unordered_map<string,long long>mp;
for(int k=0;k<n;k++){
string tmp="";
for(int ii=0;ii<m;ii++){
if((ii==i)||(ii==j))continue;
tmp+=s[k][ii];
}
mp[tmp]=mp[tmp]+1;
}
z=0;
unordered_map<string,long long>::iterator itr=mp.begin();
while(itr!=mp.end()){
y=(*itr).second;
z+=(y*(y-1))/2;
itr++;
}
if(i!=j)ans+=z;
else ans-=(m-2)*z;
}
}
unordered_map<string,long long>mp;
for(int k=0;k<n;k++)mp[s[k]]+=1;
z=0;
unordered_map<string,long long>::iterator itr=mp.begin();
while(itr!=mp.end()){
y=(*itr).second;
z+=(y*(y-1))/2;
itr++;
}
ans+=(m-1)*(m-2)/2*z;
cout<<ans<<endl;
return 0;
}
