514.自由之路

一个字符串 ring ,表示刻在外环上的编码;给定另一个字符串 key ,表示需要拼写的关键词。您需要算出能够拼写关键词中所有字符的最少步数。

最初,ring 的第一个字符与 12:00 方向对齐。您需要顺时针或逆时针旋转 ring 以使 key 的一个字符在 12:00 方向对齐,然后按下中心按钮,以此逐个拼写完 key 中的所有字符。

旋转 ring 拼出 key 字符 key[i] 的阶段中:

您可以将 ring 顺时针或逆时针旋转 一个位置 ,计为1步。旋转的最终目的是将字符串 ring 的一个字符与 12:00 方向对齐,并且这个字符必须等于字符 key[i] 。
如果字符 key[i] 已经对齐到12:00方向,您需要按下中心按钮进行拼写,这也将算作 1 步。按完之后,您可以开始拼写 key 的下一个字符(下一阶段), 直至完成所有拼写。

对于字符串key,每一位都需要一次确定,字符串ring总共n位,当前位置i,最多需要向前或向后搜索n/2次

int findRotateSteps(char* ring, char* key) {
    int r = 0, count = 0;
    int i = 0, j = 0, k = 0, n = 0;
    r = strlen(ring);
    k = strlen(key); //求字符串长度
    count = k;
    for (n = 0; n < k; n++) { //对key的每个字母
        printf(" %c", key[n]);
        //当前ring[i]
        for (j = 0; j <= r / 2; j++) { //转j次
            if (ring[(i + j) % r] == key[n]) {
                printf("%d",j);
                count += j;
                i = (i + j) % r;
                printf("%c",ring[i]);
                break;
            }
            if (ring[(r + i - j) % r] == key[n]) {
                printf("%d",j);
                count += j;
                i = (r + i - j) % r;
                printf("%c",ring[i]);
                break;
            }
        }
    }
    return count;
}

解答错误实例
ring=“nyngl”
key=“yyynnnnnnlllggg”
由于n可以向前也可以向后,但是到l最近的n应该向前的n

定义 dp[i][j]表示从前往后拼写出 key的第 i个字符, ring的第 j个字符与 12:00方向对齐的最少步数(下标均从 0开始)。

显然,只有当字符串 ring的第 j个字符需要和 key的第 i个字符相同时才能拼写出 key的第 i个字符,因此对于 key的第 i个字符,需要考虑计算的 ring的第 j个字符只有 key[i]在 ring中出现的下标集合。我们对每个字符维护一个位置数组 pos[i],表示字符 i在 ring中出现的位置集合,用来加速计算转移的过程。

对于状态 dp[i][j],需要枚举上一次与 12:00方向对齐的位置 k,因此可以列出如下的转移方程:

dp[i][j]=min⁡k∈pos[key[i−1]]{dp[i−1][k]+min⁡{abs(j−k),n−abs(j−k)}+1}
其中 min⁡{abs(j−k),n−abs(j−k)}+1表示在当前第 k个字符与 12:00方向对齐时第 j个字符旋转到 12:00方向并按下拼写的最少步数。

最后答案即为 min⁡i=0n−1{dp[m−1][i]}。

这样的做法需要开辟 O(mn)的空间来存放 dp值。考虑到每次转移状态 dp[i][]只会从 dp[i−1][]转移过来,因此我们可以利用滚动数组优化第一维的空间复杂度,有能力的读者可以尝试实现。下面只给出最朴素的 O(mn)空间复杂度的实现。

int findRotateSteps(char* ring, char* key) {
    int n = strlen(ring), m = strlen(key);
    int pos[26][n], posSize[26];
    memset(posSize, 0, sizeof(posSize));
    for (int i = 0; i < n; ++i) {
        int x = ring[i] - 'a';
        pos[x][posSize[x]++] = i;
    }
    int dp[m][n];
    memset(dp, 0x3f3f3f3f, sizeof(dp));
    for (int i = 0; i < posSize[key[0] - 'a']; i++) {
        int x = pos[key[0] - 'a'][i];
        dp[0][x] = fmin(x, n - x) + 1;
    }
    for (int i = 1; i < m; ++i) {
        for (int j = 0; j < posSize[key[i] - 'a']; ++j) {
            int x = pos[key[i] - 'a'][j];
            for (int k = 0; k < posSize[key[i - 1] - 'a']; ++k) {
                int y = pos[key[i - 1] - 'a'][k];
                dp[i][x] = fmin(dp[i][x], dp[i - 1][y] + fmin(abs(x - y), n - abs(x - y)) + 1);
            }
        }
    }
    int ret = dp[m - 1][0];
    for (int i = 1; i < n; ++i) {
        ret = fmin(ret, dp[m - 1][i]);
    }
    return ret;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶雨莳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值