hihocoder #1052 基因工程
传送门:基因工程
这道题拖了好久,一直没有清晰的思路。
当然,$k\le\frac{n}{2}$ 时,比较简单。下面我着重讲一下当 $k>\frac{n}{2}$ ,即前 $k$ 个字符与后 $k$ 个字符有重叠时,如何思考这个问题。
为了便于分析,我们把题目要求形式化成如下的数学表示
假设修改后的字符串为 $S$ ,字符串长度为 $n$ ,则 $S$ 满足
\[S_i = S_{i+n-k} \qquad 1 \le i \le k \]
即“$S$是以$n-k$为周期的字符串”。
这样讲对吗?我们回忆一下数学上周期函数的概念,不难发现这个说法不确切,一个有周期性的字符串是无限长的。
为了消除这种数学上的不严格,我们换一种说法
满足
\[S_i = S_{i+n-k} \qquad 1 \le i \le k\]
且长为$n$的字符串$S$,必定是某个以 $n-k$ 为周期的无限长字符串 $T$ 的子串。
至此我们找到了一个将问题大大简化了的必要条件,显然这个命题反过来也成立。因而有
对于任意长为 $n$ 的字符串 $S$
$S_i = S_{n-k+i} \qquad 1 \le i \le k, \quad 0 \le k \le n,$
$\iff$ $S$ 是某个以 $n-k$ 为周期的无限长字符串 $T$ 的子串
UPDATE (2019/5/16)
另一道跟周期串有关的字符串构造题,CF1158B The minimal unique substring。
$\mathsf{UPD (2018/12/27)}$
多年以后又遇到一个类似的问题,CF1081H Palindromic Magic,想起这篇旧文。
作者(fjzzq2002)在题解中也定义了周期串,把我所谓「$S$ 是某个以 $t$ 为周期的无限长字符串 $T$ 的子串」径称为「$S$ 以 $t$ 为周期($S$ has a period of length $t$)」。
现把题解中的一些术语和定义摘录在此。
问题转化为:求将一个字符串 $S$ 转化为某个以 $n-k$ 为周期的无限长字符串 $T$ 的子串,所需的最少更改次数。
这个问题思考起来可比原问题清楚多了,而且至此我们已经把开头说到的两种情况统一起来了。
可以通过频数统计求解:
分别统计
\[1, 1+n-k, 1+2(n-k), \dots \]
\[2, 2+n-k, \dots\]
\[\cdots\]
\[n-k, n-k+n-k, \dots\]
上A, G, C, T出现的频数,将其改成频数最大的那个字符,这样所需的总改动次数就是答案。
P.S. 这篇随笔是我看了李舜阳的 hihoCoder #1052 基因工程 后写的。看他画的图还是不能完全把握这个问题,我觉得从数学上将问题形式化,寻找能够简化问题的必要条件,对我们分析问题极有帮助,也是一种科学的思维方式。我们即使不画图也能透彻地分析这个问题,相反只看李舜阳的图而不借助形式化的推导仍是糊里糊涂。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAX_N=1e3+10; 4 char s[MAX_N]; 5 const char* item="ACGT"; 6 int main(){ 7 //freopen("in", "r", stdin); 8 int T, K, N, rep, ans, maxi, cnt[4]; //A, C, G, T 9 scanf("%d", &T); 10 while(T--){ 11 scanf("%s%d", s+1, &K); 12 N=strlen(s+1); 13 rep=N-K; 14 ans=0; 15 for(int i=1; i<=rep; i++){ 16 memset(cnt, 0, sizeof(cnt)); 17 for(int j=i; j<=N; j+=rep){ 18 for(int k=0; k<4; k++){ 19 if(s[j]==item[k]){ 20 cnt[k]++; 21 break; 22 } 23 } 24 } 25 maxi=0; 26 for(int j=0; j<4; j++){ 27 maxi=max(maxi, cnt[j]); 28 ans+=cnt[j]; 29 } 30 ans-=maxi; 31 } 32 printf("%d\n", ans); 33 } 34 return 0; 35 }