Skip to content

数据结构与算法 - 字符串中的基本应用 #26

@wangjing013

Description

@wangjing013

基本算法技能

反转字符串

在 JS 中反转字符串直接调用现成API即可:

function reverse() {
  // 定义被反转的字符串
  const str = 'wangjing'
  // 定义反转后的字符串
  const res = str.split('').reverse().join('')
  console.log(res) // jingwang
}

判断字符串是不是回文

回文概念: 正反读的时都是一样的字符串. 如下:

'abcba'

下面我们来实现判断字符串是不是回文:

function isPalindrome(str) {
  const reverseStr = reverse(str);
  return reverseStr === str
}

同时我们可以利用对称性原则来解决这个问题.

function isPalindrome(str) {
    const len = str.length;
    for (let i = 0; i < len / 2; i++){
      if (str[i] !== str[len - i - 1]) {
        return false
      }
    }
    return true;
  }

面试题

真题描述: 给定一个非空字符串 s, 最多删除一个字符. 判断是否能成为回文字符串.

示例:

// 输入
"abcdecba"
// 输出
true
// 删除 d or e

// 输入
"abeccba"
// 输出
true
// 可以删除 e 得到回文

题干中可以获取信息, 可以通过删除一个字符的方式来判断字符串是不是回文. 最简单的解决方案就是: 逐个删除, 判断删除后的字符串是不是回文.

 function main() {
    let str = "abcdfddcba";
    let len = str.length;
    if(isPalindrome(str)) {
      console.log("是回文");
      return;
    }
    for (let i = 0; i < len; i++) {
      let arr = str.split('');
      arr.splice(i, 1);
      if (isPalindrome(arr.join())) {
        console.log('是回文');
        return;
      }
    }
    console.log("不是回文");
  }

  function isPalindrome(str) {
    const len = str.length;
    for (let i = 0; i < len / 2; i++){ // n/2
      if (str[i] !== str[len - i - 1]) {
        return false
      }
    }
    return true;
  }
  main();

上述方式实现了, 但是效率不高, 衡量效率高低通过时间复杂度, 上面的实现复杂度为 O(n^2). 有没有更高效的方式? 有

针对回文类的首先应该想到对称性双向指针. 下面通过双向指针的实现方式:

双向指针

下面通过代码来实现:

  function validPalindrome(str) {
    // 先保存两个指针,分别是头指针、尾指针
    let start = 0;
    let end = str.length - 1;
    // 第一个条件表示循环终止条件,防止指针碰撞
    // 第二个为当只要出现不等时也应该终止
    while (start < end && str[start] === str[end]) {
      start++;
      end--;
    }

    // 左指针移动
    if (isPalindrome(start++, end)) {
      return true;
    }

    // 右指针移动
    if (isPalindrome(start, end--)) {
      return true;
    }

    function isPalindrome(st, ed) {
      while (st < ed) {
        if (str[st] != str[ed]) {
          return false;
        }
        st++;
        end--;
      }
      return true;
    }
    return false;
  }

  let bool = validPalindrome("abcdfefgdcba");
  console.log(bool)

真题描述: 计一个支持以下两种操作的数据结构

void addWord(word){...}
bool search(word){...}

其中查询时支持搜索普通字符串或正则表达式字符串, 字符串只包含字母 .a-z. . 可以表示任何一个字母.

从题目中应该考虑几个问题:

  • 数据如何存储
  • 如何区分普通字符串或正则表达式字符串

通常存储数据考虑用 数组、Map 等. 在这道题中通过 Map 来实现, 至于为什么通过实现后再来看这个问题.

如何区分普通字符串或正则表达式? 判断 word 是否包含 . 字符

下面我们通过代码来实现:

const WordDictionary = function() {
  // 主要用来储存单词
  this.words = {}
}

WordDictionary.prototype.addWord = function(word) {
  // 边界检查
  if (!word) { return; }
  const len = word.length;
  // 先判断数组中有没有存在相同长度的
  if (!this.words[len]) {
    this.words[len] = [word]
  } else {
    this.words[len].push(word);
  }
}
WordDictionary.prototype.search = function(word) {
  if (!this.words[word.length]) {
    return;
  }
  // 缓存目标元素的长度
  const len = word.length;

  // word 是不是包含点"."(区分普通字符串和正则字符串)
  if (!word.containes('.')) {
    return this.words[len].includes(word);
  }

  // 创建正则表达式
  const regex = new Regex(word);
  return this.words[len].some((item) => regex.test(item))
}

回到最开始选用数据结构的问题, 为什么用 Map ? 在储存时我们根据 length 作为Map 的 key, 在搜索时可以有效的缩小查找的范围,从而提高其查找的效率.

真题描述:请你来实现一个 atoi 函数,使其能将字符串转换成整数.

atoi 函数有什么特性:

  • atoi 函数 会跳过前面的空白字符.
  • 如果输入的字符串为空 或 不能转换为整数时则直接返回 0.
  • atoi 返回整数有大小限制, 也就是在整数范围之内, 否则也会返回 0.

其实特性就是代码文字描述, 下面通过代码来实现:

function atoi(str) {
  // 主要通过正则来实现的, 主要匹配到分组中表达式内容就是我们要到内容
  const regex = /^\s*([+\--]?\d*).*/;
  const groups = str.match(regex);

  // 获取分组表达式中内容
  let result = 0;
  if (groups) {
    result = +groups[1];
    if (isNaN(result)) {
      result = 0;
    }
  }

  // 范围判断
  const max = Math.pow(2, 31) - 1;
  const min = - max - 1;
  if (result < min || result > max) {
    return 0;
  }
  // 返回结果
  return result;
}
console.log(atoi(' 58')); //=> 58
console.log(atoi(' abcded58')); //=> 0

总结

  • 什么是回文? 字符串正序和逆序读取内容相等
  • 判断回文几种方式
    • 字符串反转
    • 对称性

通过前面题目来看, 解决回文类似题目我们首先应该想到对称性双指针方法来实现.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions