📌  相关文章
📜  计算通过执行 K 次循环移位将字符串S 转换为 T 的方法数(1)

📅  最后修改于: 2023-12-03 15:12:05.361000             🧑  作者: Mango

计算通过执行 K 次循环移位将字符串S 转换为 T 的方法数

在本文中,我们将讨论如何计算通过执行 K 次循环移位将字符串 S 转换为 T 的方法数。

什么是循环移位?

循环移位是指将字符序列中的每个字符向左或向右移动 k 个位置,其中 k 是一个固定的整数,如果从字符串序列的起始位置开始移位,就称这种移位为循环移位。

例如,对于字符串 "abcde",如果我们执行 2 次循环左移操作,那么我们将得到字符串 "cdeab"。如果我们执行 3 次循环右移操作,那么我们将得到字符串 "cdeab"。

如何计算方法数?

如果我们已经知道了所有可能的字符串 T,那么我们可以计算从字符串 S 转换为字符串 T 的方法数。假设字符串 S 和字符串 T 的长度均为 n,那么我们可以将字符串 S 分别旋转 0,1,...,n-1 个位置,得到 n 种变换后的字符串。然后,我们可以计算每个变换后的字符串与字符串 T 之间的编辑距离,编辑距离是指将一个字符串转换为另一个字符串所需的最小操作数。最后,我们将所有编辑距离相等的字符串的计数器相加,得到从字符串 S 转换为字符串 T 的方法数。

但是,计算所有可能的字符串 T 是不可行的。因此,我们需要另一种方法来计算从字符串 S 转换为字符串 T 的方法数。

假设我们已经知道字符串 S 经过 k 次循环移位后得到字符串 T。我们可以发现,如果我们将字符串 T 再次循环移位 k 次,那么我们将得到字符串 S。因此,k 次循环移位等价于将字符串 S 和字符串 T 旋转到彼此对应的位置。具体来说,我们可以用字符串 S 的第 i 个字符对应字符串 T 的第 p(i) 个字符,其中 p(i) = (i + k) mod n。例如,如果我们对字符串 "abcde" 执行 2 次循环左移操作,那么字符串 S 变为 "cdeab",字符串 T 变为 "eabcd"。我们可以用以下映射将它们对应起来:

S: cdeab
T: eabcd
p: 01234

我们可以看到,S 的第 0 个字符('c')对应 T 的第 2 个字符('e'),S 的第 1 个字符('d')对应 T 的第 3 个字符('a'),以此类推。然后,我们可以将这个映射看作一个循环置换,例如上面的映射就可以用下面的循环置换表示:

(0)(1 2 3 4)

这个置换将 0 映射到 2, 1 映射到 3,2 映射到 4,3 映射到 0,4 映射到 1。在一般情况下,我们可以用图形表示一个循环置换。例如,下面的图形表示了上面的循环置换:

 0 -> 2 -> 4 -> 1 -> 3 -> 0
 o----->o----->o----->o----->o
   (1)    (2)    (3)    (4)

现在,我们可以使用置换群的概念来计算从字符串 S 转换为字符串 T 的方法数。置换群是指一组置换的集合,满足它们满足群的性质,即它们是可逆的、封闭的和结合的。

我们可以定义一个置换群 G,它由所有将字符串 S 旋转 k 个位置得到的置换组成。对于 G 中的任意两个置换 g 和 h,我们可以定义它们的乘积 gh 为先应用 h 后应用 g 得到的置换。例如,如果我们对字符串 "abcde" 执行 2 次循环左移操作,那么 G 中的置换分别为:

S: abcde
G: (0)(1 2 3 4)  (1)(2 3 4 0)  (2)(3 4 0 1)  (3)(4 0 1 2)  (4)(0 1 2 3)

现在,假设我们已经知道字符串 S 经过 k 次循环移位后得到了字符串 T。我们可以将这个映射看作一个置换 g,例如上面的映射可以用以下置换表示:

g: (0 p(0))(1 p(1))(2 p(2))(3 p(3))(4 p(4))

我们可以将 g 应用到 G 中的每个置换上,得到另一个置换 h。例如,如果我们将 g 应用到 G 中的置换 (1)(2 3 4 0) 上,我们将得到以下置换:

h: (1 p(1))(2 p(2))(3 p(3))(4 p(4))(0 p(0))

我们可以看到,h 的作用是将字符串 T 从第 k+1 个字符开始切割成 T1 和 T2 两个部分,然后将 T2 和 T1 连接起来。例如,如果字符串 T 为 "eabcd",那么 T1 为 "eab",T2 为 "cd",则 h 将字符串 T 转换为 "abcde",即将 T1 和 T2 颠倒过来。

因此,如果我们可以计算出 G 中有多少个置换与 h 相等,那么从字符串 S 转换为字符串 T 的方法数就是 G 中与 g 相等的置换的个数。我们可以使用 Burnside 引理计算这个数量,Burnside 引理的基本思想是计算置换群中所有元素的不动点的平均数。具体来说,如果我们定义一个置换 f 的不动点集合 Fix(f) 为使置换 f 作用后不发生改变的元素的集合,那么 Burnside 引理的计算公式为:

|G/g| = (1/|G|) Σ(f in G) |Fix(fg)|

其中 |G/g| 表示 G 中与 g 相等的置换的个数,|G| 表示 G 中置换的总数,Σ(f in G) 表示对 G 中的所有置换 f 求和。

现在,我们可以确定 G 中所有置换的总数,即 |G| = n。因为每个置换 g 都可以对应到一个置换 h,而 h 的不动点集合 Fix(h) 就是字符串 T 从第 k+1 个字符开始切割成 T1 和 T2 两个部分时,T2 与 T1 的旋转等于 k 的字符串的个数。因此,我们可以用以下公式计算从字符串 S 转换为字符串 T 的方法数:

count = (1/n) Σ(f in G) count(f)

其中 count(f) 表示与置换 f 相等的字符串的个数,可以通过枚举字符串 S 的每个字符,并将它们通过置换 f 映射到字符串 T 上,然后检查是否与字符串 T 相等来计算。

程序实现

以下是一个简单的 Python 实现,用于计算从字符串 S 转换为字符串 T 的方法数:

def count_rotation(S, T, K):
    n = len(S)
    G = [(S[i:] + S[:i], T[i:] + T[:i]) for i in range(n)]
    count = 0
    for f in G:
        h = (''.join(f[1][i] for i in map(lambda x: (x + K) % n, range(n))), f[1])
        c = sum(1 for i in range(n) if h[0] == h[1][i:] + h[1][:i])
        count += c / n
    return count

该函数接受三个参数:字符串 S、字符串 T 和循环移位的次数 K。它返回从字符串 S 转换为字符串 T 的方法数。

例如,如果我们要将字符串 "abcde" 转换为字符串 "eabcd",并执行 2 次循环左移操作,则可以执行以下代码:

S = 'abcde'
T = 'eabcd'
K = 2
count = count_rotation(S, T, K)
print(count)

该代码输出的结果应为 2.0,因为除了原始字符串 T 外,这个字符串可以通过旋转 0 或 2 个位置得到。