统计追加字母可以获得的单词数

统计追加字母可以获得的单词数

题目地址: https://leetcode-cn.com/problems/count-words-obtained-after-adding-a-letter/

题目描述

给你两个下标从 0 开始的字符串数组 startWordstargetWords 。每个字符串都仅由 小写英文字母 组成。

对于 targetWords 中的每个字符串,检查是否能够从 startWords 中选出一个字符串,执行一次 转换操作 ,得到的结果与当前 targetWords 字符串相等。

转换操作 如下面两步所述:

  1. 追加任何不存在于当前字符串的任一小写字母到当前字符串的末尾。
    • 例如,如果字符串为 "abc" ,那么字母 'd''e''y' 都可以加到该字符串末尾,但 'a' 就不行。如果追加的是 'd' ,那么结果字符串为 "abcd"
  2. 重排新字符串中的字母,可以按任意顺序重新排布字母。
    • 例如,"abcd" 可以重排为 "acbd""bacd""cbda",以此类推。注意,它也可以重排为 "abcd" 自身。

找出 targetWords 中有多少字符串能够由 startWords 中的 任一 字符串执行上述转换操作获得。返回 targetWords 中这类 字符串的数目

**注意:**你仅能验证 targetWords 中的字符串是否可以由 startWords 中的某个字符串经执行操作获得。startWords 中的字符串在这一过程中 发生实际变更。

示例 1:

输入:startWords = ["ant","act","tack"], targetWords = ["tack","act","acti"]
输出:2
解释:
- 为了形成 targetWords[0] = "tack" ,可以选用 startWords[1] = "act" ,追加字母 'k' ,并重排 "actk" 为 "tack" 。
- startWords 中不存在可以用于获得 targetWords[1] = "act" 的字符串。
  注意 "act" 确实存在于 startWords ,但是 必须 在重排前给这个字符串追加一个字母。
- 为了形成 targetWords[2] = "acti" ,可以选用 startWords[1] = "act" ,追加字母 'i' ,并重排 "acti" 为 "acti" 自身。

示例 2:

输入:startWords = ["ab","a"], targetWords = ["abc","abcd"]
输出:1
解释:
- 为了形成 targetWords[0] = "abc" ,可以选用 startWords[0] = "ab" ,追加字母 'c' ,并重排为 "abc" 。
- startWords 中不存在可以用于获得 targetWords[1] = "abcd" 的字符串。

提示:

  • 1 <= startWords.length, targetWords.length <= 5 * 104
  • 1 <= startWords[i].length, targetWords[j].length <= 26
  • startWordstargetWords 中的每个字符串都仅由小写英文字母组成
  • startWordstargetWords 的任一字符串中,每个字母至多出现一次

思路:

字符串的提示范围中有一条 在 startWordstargetWords 的任一字符串中,每个字母至多出现一次。那么熟悉位运算的就会有如下思路

  • 字符数组 a - z 一共有26位,一个int是32位 那么是不是可以用int来代替string
    • 比如 abc 那么对应就是int 32位中第0位,第1位,第1位为1,即转化为二进制就是 0111也就是数字7
    • 长度相同字符串不同的字符对应的数字是不同,因为他们在int中1的位置不同,
    • 长度相同字符串仅仅顺序不同对应的数字相同,也就是说abcbac对应的数字是一样的
  • 转换操作中,第一个操作就是新增一个字符,比如 abc -> abcd,那么怎么通过数位转换来通过abcd找到abc
    • abc 转化为二进制是 000000111, abcd 转化为二进制是 00001111, 那么很容易得到 abc ^ abcd = 00000111 ^ 0000111100001000, 那么就是在二进制中判断一个数是否只有一位为1,
    • 在二进制运算中有一种比较简单的方案即 a & a-1如果得0,那么代表这个数字的二进制只有一位为1.
    • 那么我们如果要获取一个二进制数,某一个数为1,那么就可以想到 a & (-a),比如 二进制运算中 a = 00001100 ,那么a & -a 则为二进制最后一个1 即为00001000,
    • abcd去掉每一位,那么总有abc这个选项
  • 既然是需要从target中找 start,要尽可能快,那么只需要将start转化为可快速索引的数据结构,用Hashset

代码

将字符串转化为数字,我们需要将a映射为0,b映射为1,那么转换部分代码可以写为

public int turnToInt(String str){
    int a = 0;
    for(int i = 0; i < str.length(); i++){
        a |= 1 << (str.charAt(i) - 'a');
    }
    return a;
}

那么最后的代码

public int wordCount(String[] startWords, String[] targetWords) {
        HashSet<Integer> set = new HashSet(startWords.length);
        for (String startWord : startWords) {
            set.add(turnToInt(startWord));
        }
        int count = 0;
        for (String targetWord : targetWords) {
            if(targetWord.length() == 1){
                continue;
            }
            int i = turnToInt(targetWord);
            int tem = i;
            while (tem != 0){
                int index = tem & (-tem);
                tem ^= index;
                if(set.contains(i ^ index)){
                    count++;
                    break;
                }
            }
        }
        return count;
    }

    public int turnToInt(String str){
        int a = 0;
        for(int i = 0; i < str.length(); i++){
            a |= 1 << (str.charAt(i) - 'a');
        }
        return a;
    }

结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值