Algorithm brushing questions - string - reverse string II

A simple inversion is not enough, I want a fancy inversion

541. Reverse a String II

link

Given a string s and an integer k, counting from the beginning of the string, every time 2k characters are counted, reverse the first k characters of the 2k characters.

If the remaining characters are less than k, reverse all remaining characters.

If the remaining characters are less than 2k but greater than or equal to k, reverse the first k characters and leave the rest as they are.

Example:

Input: s = "abcdefg", k = 2
Output: "bacdfeg"

train of thought

This question is actually a simulation, and it is enough to implement the reversal rules stipulated in the question.

Some students may write a bunch of logic codes or set up a counter for the first k characters of every 2k characters to count 2k characters, and then count the first k characters.

In fact, in the process of traversing the string, just let i += (2 * k), i move 2 * k each time, and then judge whether there is a need for an inversion interval.

Because what you want to find is the starting point of every 2*k interval, so the program will be much more efficient when written in this way.

So when you need to process strings one by one with a fixed rule, think about making a fuss about the expression in the for loop.

The performance is as follows:

So for the specific inversion logic here, should we use library functions? In fact, it doesn’t matter if it’s practical or not. There’s nothing wrong with using reverse to achieve inversion.

C++ code

The version using the C++ library function reverse is as follows:

class Solution {
public:
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= s.size()) {
                reverse(s.begin() + i, s.begin() + i + k );
            } else {
                // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};

Then we can also implement our own reverse function, which is actually the same as topic 344. Reversing a string .

The reverse function interval I implemented below is a left-closed right-closed interval, and the code is as follows:

class Solution {
public:
    void reverse(string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= s.size()) {
                reverse(s, i, i + k - 1);
                continue;
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
            reverse(s, i, s.size() - 1);
        }
        return s;
    }
};

Another way of thinking

class Solution {
public:
    string reverseStr(string s, int k) {
        int n = s.size(),pos = 0;
        while(pos < n){
            //剩余字符串大于等于k的情况
            if(pos + k < n) reverse(s.begin() + pos, s.begin() + pos + k);
            //剩余字符串不足k的情况 
            else reverse(s.begin() + pos,s.end());
            pos += 2 * k;
        }
        return s;
    }
};

other language versions

C:

char * reverseStr(char * s, int k){
    
    
    int len = strlen(s);

    for (int i = 0; i < len; i += (2 * k)) {
    
    
        //判断剩余字符是否少于 k
        k = i + k > len ? len - i : k;

        int left = i;
        int right = i + k - 1;
        while (left < right) {
    
    
            char temp = s[left];
            s[left++] = s[right];
            s[right--] = temp;
        }
    }

    return s;
}

Java:

//解法一
class Solution {
    public String reverseStr(String s, int k) {
        StringBuffer res = new StringBuffer();
        int length = s.length();
        int start = 0;
        while (start < length) {
            // 找到k处和2k处
            StringBuffer temp = new StringBuffer();
            // 与length进行判断,如果大于length了,那就将其置为length
            int firstK = (start + k > length) ? length : start + k;
            int secondK = (start + (2 * k) > length) ? length : start + (2 * k);

            //无论start所处位置,至少会反转一次
            temp.append(s.substring(start, firstK));
            res.append(temp.reverse());

            // 如果firstK到secondK之间有元素,这些元素直接放入res里即可。
            if (firstK < secondK) { //此时剩余长度一定大于k。
                res.append(s.substring(firstK, secondK));
            }
            start += (2 * k);
        }
        return res.toString();
    }
}

//解法二(似乎更容易理解点)
//题目的意思其实概括为 每隔2k个反转前k个,尾数不够k个时候全部反转
class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();
        for(int i = 0; i < ch.length; i += 2 * k){
            int start = i;
            //这里是判断尾数够不够k个来取决end指针的位置
            int end = Math.min(ch.length - 1, start + k - 1);
            //用异或运算反转 
            while(start < end){
                ch[start] ^= ch[end];
                ch[end] ^= ch[start];
                ch[start] ^= ch[end];
                start++;
                end--;
            }
        }
        return new String(ch);
    }
}


// 解法二还可以用temp来交换数值,会的人更多些
class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();
        for(int i = 0;i < ch.length;i += 2 * k){
            int start = i;
            // 判断尾数够不够k个来取决end指针的位置
            int end = Math.min(ch.length - 1,start + k - 1);
            while(start < end){
                
                char temp = ch[start];
                ch[start] = ch[end];
                ch[end] = temp;

                start++;
                end--;
            }
        }
        return new String(ch);
    }
}
// 解法3
class Solution {
    
    
    public String reverseStr(String s, int k) {
    
    
        char[] ch = s.toCharArray();
        // 1. 每隔 2k 个字符的前 k 个字符进行反转
        for (int i = 0; i< ch.length; i += 2 * k) {
    
    
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= ch.length) {
    
    
                reverse(ch, i, i + k -1);
                continue;
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转
            reverse(ch, i, ch.length - 1);
        }
        return  new String(ch);

    }
    // 定义翻转函数
    public void reverse(char[] ch, int i, int j) {
    
    
    for (; i < j; i++, j--) {
    
    
        char temp  = ch[i];
        ch[i] = ch[j];
        ch[j] = temp;
    }

    }
}

Python:

class Solution:
    def reverseStr(self, s: str, k: int) -> str:
        """
        1. 使用range(start, end, step)来确定需要调换的初始位置
        2. 对于字符串s = 'abc',如果使用s[0:999] ===> 'abc'。字符串末尾如果超过最大长度,则会返回至字符串最后一个值,这个特性可以避免一些边界条件的处理。
        3. 用切片整体替换,而不是一个个替换.
        """
        def reverse_substring(text):
            left, right = 0, len(text) - 1
            while left < right:
                text[left], text[right] = text[right], text[left]
                left += 1
                right -= 1
            return text
        
        res = list(s)

        for cur in range(0, len(s), 2 * k):
            res[cur: cur + k] = reverse_substring(res[cur: cur + k])
        
        return ''.join(res)

Python3 (v2):

class Solution:
    def reverseStr(self, s: str, k: int) -> str:
        # Two pointers. Another is inside the loop.
        p = 0
        while p < len(s):
            p2 = p + k
            # Written in this could be more pythonic.
            s = s[:p] + s[p: p2][::-1] + s[p2:]
            p = p + 2 * k
        return s

Go:

func reverseStr(s string, k int) string {
    
    
    ss := []byte(s)
    length := len(s)
    for i := 0; i < length; i += 2 * k {
    
    
     // 1. 每隔 2k 个字符的前 k 个字符进行反转
     // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
        if i + k <= length {
    
    
            reverse(ss[i:i+k])
        } else {
    
    
            reverse(ss[i:length])
        }
    }
    return string(ss)
}

func reverse(b []byte) {
    
    
    left := 0
    right := len(b) - 1
    for left < right {
    
    
        b[left], b[right] = b[right], b[left]
        left++
        right--
    }
}

javaScript:

/**
 * @param {string} s
 * @param {number} k
 * @return {string}
 */
var reverseStr = function(s, k) {
    
    
    const len = s.length;
    let resArr = s.split(""); 
    for(let i = 0; i < len; i += 2 * k) {
    
      // 每隔 2k 个字符的前 k 个字符进行反转
        let l = i - 1, r = i + k > len ? len : i + k;
        while(++l < --r) [resArr[l], resArr[r]] = [resArr[r], resArr[l]];
    }
    return resArr.join("");
};

TypeScript:

function reverseStr(s: string, k: number): string {
    
    
    let left: number, right: number;
    let arr: string[] = s.split('');
    let temp: string;
    for (let i = 0, length = arr.length; i < length; i += 2 * k) {
    
    
        left = i;
        right = (i + k - 1) >= length ? length - 1 : i + k - 1;
        while (left < right) {
    
    
            temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
            left++;
            right--;
        }
    }
    return arr.join('');
};

Swift:

func reverseStr(_ s: String, _ k: Int) -> String {
    
    
    var ch = Array(s)

    for i in stride(from: 0, to: ch.count, by: 2 * k) {
    
    
        var left = i
        var right = min(s.count - 1, left + k - 1)
      
        while left < right {
    
    
            (ch[left], ch[right]) = (ch[right], ch[left])
            left += 1
            right -= 1
        }
    }
    return String(ch)
}

C#:

public class Solution
{
    
    
    public string ReverseStr(string s, int k)
    {
    
    
        Span<char> span = s.ToCharArray().AsSpan();
        for (int i = 0; i < span.Length; i += 2 * k)
        {
    
    
            span[i + k < span.Length ? i..(i + k) : i..].Reverse();
        }
        return span.ToString();
    }
}

Scala:

Version 1: (normal solution)

object Solution {
    
    
  def reverseStr(s: String, k: Int): String = {
    
    
    val res = s.toCharArray // 转换为Array好处理
    for (i <- s.indices by 2 * k) {
    
    
      // 如果i+k大于了res的长度,则需要全部翻转
      if (i + k > res.length) {
    
    
        reverse(res, i, s.length - 1)
      } else {
    
    
        reverse(res, i, i + k - 1)
      }
    }
    new String(res)
  }
  // 翻转字符串,从start到end
  def reverse(s: Array[Char], start: Int, end: Int): Unit = {
    
    
    var (left, right) = (start, end)
    while (left < right) {
    
    
      var tmp = s(left)
      s(left) = s(right)
      s(right) = tmp
      left += 1
      right -= 1
    }
  }
}

Version 2: First use grouped to divide every k, then use zipWithIndex to add the index of each array, and then use map to perform transformation. If the index %2==0, it means that it needs to be flipped, otherwise it will remain unchanged, and finally convert for String

object Solution {
    
    
  def reverseStr(s: String, k: Int): String = {
    
    
    // s = "abcdefg", k = 2
    s.grouped(k) // Iterator ["ab", "cd", "ef", "g"]
      .zipWithIndex // Iterator [("ab", 0), ("cd", 1), ("ef", 2), ("g", 3)]
      .map {
    
    
        case (subStr, index) => 
          if (index % 2 == 0) subStr.reverse else subStr
      }
      .mkString
  }
}

Version three: (recursive)

import scala.annotation.tailrec

object Solution {
    
    
  def reverseStr(s: String, k: Int): String = {
    
    
    @tailrec // 这个函数已经优化成了尾递归
    def reverse(s: String, needToReverse: Boolean, history: String): String = {
    
    
      // 截取前k个字符(判断是否翻转)
      val subStr = if (needToReverse) s.take(k).reverse else s.take(k)
      // 如果字符串长度小于k,返回结果
      // 否则,对于剩余字符串进行同样的操作
      if (s.length < k) history + subStr
      else reverse(s.drop(k), !needToReverse, history + subStr)
    }
    reverse(s, true, "")
  }
}

Rust:

impl Solution {
    pub fn reverse(s: &mut Vec<char>, mut begin: usize, mut end: usize){
        while begin < end {
            let temp = s[begin];
            s[begin] = s[end];
            s[end] = temp;
            begin += 1;
            end -= 1;
        }
    }
    pub fn reverse_str(s: String, k: i32) -> String {
        let len = s.len();
        let k = k as usize;
        let mut s = s.chars().collect::<Vec<_>>();
        for i in (0..len).step_by(2 * k) {
            if i + k < len {
                Self::reverse(&mut s, i, i + k - 1);
            }
            else {
                Self::reverse(&mut s, i, len - 1);
            }
        }
        s.iter().collect::<String>()
    }
}

Guess you like

Origin blog.csdn.net/weixin_42439274/article/details/131265449