数据结构--LeetCode专题练习 Day14

43. 字符串相乘

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

示例 1:

输入: num1 = "2", num2 = "3"
输出: "6"

示例 2:

输入: num1 = "123", num2 = "456"
输出: "56088"


说明:

num1 和 num2 的长度小于110。
num1 和 num2 只包含数字 0-9。
num1 和 num2 均不以零开头,除非是数字 0 本身。
不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。

原始思路:

1.将字符串化为数组 2.对应位相乘 3.将结果改为字符串

(对应位相乘没有思考清楚,即如何相乘,后文提到的补零)

思路更正:

法一:做加法
1.如果num 1和num2之一是0,则直接将0作为结果返回即可。
2.如果num 1和num2都不是0,通过竖式乘法计算。
从左往右遍历乘数,将乘数(num2)的每一位与被乘数(num1)相乘得到对应结果,再将每次得到的结果累加
num2处理最低位,其余的每一位的运算结果都需要补0

class Solution {
public:
	
    string multiply(string num1, string num2) {
		//为0的情况
        if (num1 == "0" || num2 == "0") {
            return "0";
        }
        string ans = "0";

        int m = num1.size(), n = num2.size();
		//从左向右遍历num2
        for (int i = n - 1; i >= 0; i--) {
            string curr;
            int add = 0;//进位
            for (int j = n - 1; j > i; j--) {
                curr.push_back(0);//push_back() 在Vector最后添加一个元素(参数为要插入的值),此处为加入0
            }
            int y = num2.at(i) - '0';//string .at()用于获取指定字符;at(i),i就是想要获取的字符的下标,函数返回值为指定的字符
			//遍历num1
            for (int j = m - 1; j >= 0; j--) {
                int x = num1.at(j) - '0';
                int product = x * y + add;//相乘+进位
                curr.push_back(product % 10);//保留余数
                add = product / 10;//进位
            }
            while (add != 0) {
                curr.push_back(add % 10);
                add /= 10;
            }
            reverse(curr.begin(), curr.end());
			// reverse函数用于反转在[first,last)范围内的顺序(包括first指向的元素,不包括last指向的元素),reverse函数没有返回值
            for (auto &c : curr) {
                c += '0';//补零
            }
            ans = addStrings(ans, curr);
        }
        return ans;
    }

    string addStrings(string &num1, string &num2) {
        int i = num1.size() - 1, j = num2.size() - 1, add = 0;
        string ans;
        while (i >= 0 || j >= 0 || add != 0) {
            int x = i >= 0 ? num1.at(i) - '0' : 0;
            int y = j >= 0 ? num2.at(j) - '0' : 0;
            int result = x + y + add;
            ans.push_back(result % 10);
            add = result / 10;
            i--;
            j--;
        }
        reverse(ans.begin(), ans.end());
        for (auto &c: ans) {
            c += '0';
        }
        return ans;
    }
};// 8ms

两个函数: multiply(string num1, string num2);和addStrings(string &num1, string &num2) ;分别实现加法功能和进位功能

用到的特殊函数:

push_back() :在Vector最后添加一个元素(参数为要插入的值)。

string .at():获取指定字符;at(i), i就是想要获取的字符的下标,函数返回值为指定的字符

reverse():反转在[first,last)范围内的顺序(包括first指向的元素,不包括last指向的元素),reverse函数没有返回值

三目运算符:

 法二:做乘法

用数组代替字符串存储结果
令m,n分别表示num1和num2的长度,并且均不为0,则他们乘积的长度为m+n-1或m+n
由此创建长度为m+n的数组ansArr用于存储乘积
num 1[i]×num2[j]的结果位于ansArr[i+j+1],如果ansArr[i+j+1]≥10,则将进位部分加到ansArr[i+j]

class Solution {
public:
    string multiply(string num1, string num2) {
        if (num1 == "0" || num2 == "0") {
            return "0";
        }
        int m = num1.size(), n = num2.size();
        auto ansArr = vector<int>(m + n);//存储乘积,大小为m+n
		//遍历num1
        for (int i = m - 1; i >= 0; i--) {
            int x = num1.at(i) - '0';
			//遍历num2
            for (int j = n - 1; j >= 0; j--) {
                int y = num2.at(j) - '0';
                ansArr[i + j + 1] += x * y;//乘积
            }
        }

        for (int i = m + n - 1; i > 0; i--) {
            ansArr[i - 1] += ansArr[i] / 10;//十位进上(到前一位)
            ansArr[i] %= 10;//余数存在该位
        }
        int index = ansArr[0] == 0 ? 1 : 0;
        string ans;
        while (index < m + n) {
            ans.push_back(ansArr[index]);
            index++;
        }
        for (auto &c: ans) {
            c += '0';
        }
        return ans;
    }
};//4ms

49. 字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母都恰好只用一次。

 

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]


示例 2:

输入: strs = [""]
输出: [[""]]


示例 3:

输入: strs = ["a"]
输出: [["a"]]
 

提示:

1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i] 仅包含小写字母

题目的意思是:将组成字母相同的单词放在一个数组中

重点:通过什么方法识别出每个单词的字母,首先明确字符串需要转化为数组

法一:排序

1.将不同的字符串转换为字符数组并按照字母顺序进行排序
2.异位词排序后的结果相同,故可以作为哈希表的key值
3.将字母异位词组成的集合作为哈希表的value值

 java版本:

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
       //判断是否为空字符串数组
        if(strs == null || strs.length == 0){
            return new ArrayList();
        }
        //1.创建一个哈希表
        Map<String,List> map = new HashMap<String, List>();
        for (String s: strs) {
            //将字符串转化为字符数组
            char[] chars = s.toCharArray();
            //对字符数组按照字母顺序排序
            Arrays.sort(chars);
            //将排序后的字符串作为哈希表中的key值
            String key = String.valueOf(chars);
            //2.判读哈希表中是否有该key值
            if (!map.containsKey(key)){
                //若不存在,则为新的异位词语,在map中创建新的键值对
                map.put(key,new ArrayList());
            }
            //3.将该字符串放在对应key的list中
            map.get(key).add(s);
        }
        //返回map中所有键值对象构成的list
        return new ArrayList(map.values());
    }
}

c++版本 

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> mp;
		//构造单词的字符排序作为键
        for (string& str: strs) {
            string key = str;
            sort(key.begin(), key.end());//排序
            mp[key].emplace_back(str);
        }
        vector<vector<string>> ans;
		//加入散列表对应位置
        for (auto it = mp.begin(); it != mp.end(); ++it) {
            ans.emplace_back(it->second);
        }
        return ans;
    }
};

法二:计数

由于互为字母异位词的两个字符串包含的字母相同,因此两个字符串中的相同字母出现的次数一定是相同的,故可以将每个字母出现的次数使用字符串表示,作为哈希表的键。
由于字符串只包含小写字母,因此对于每个字符串,可以使用长度为 26的数组记录每个字母出现的次数。

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        // 自定义对 array<int, 26> 类型的哈希函数
        auto arrayHash = [fn = hash<int>{}] (const array<int, 26>& arr) -> size_t {
            return accumulate(arr.begin(), arr.end(), 0u, [&](size_t acc, int num) {
                return (acc << 1) ^ fn(num);
            });
        };

        unordered_map<array<int, 26>, vector<string>, decltype(arrayHash)> mp(0, arrayHash);
        for (string& str: strs) {
            array<int, 26> counts{};
            int length = str.length();
            for (int i = 0; i < length; ++i) {
                counts[str[i] - 'a'] ++;
            }
            mp[counts].emplace_back(str);
        }
        vector<vector<string>> ans;
        for (auto it = mp.begin(); it != mp.end(); ++it) {
            ans.emplace_back(it->second);
        }
        return ans;
    }
};

法三:暴力求解

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> result = new ArrayList<>();

        int len = strs.length;
        String[] strings = new String[len];

//将排序后的dest字符串数组放到一个新的字符串数组中
for (int i = 0; i < len; i++) {
    byte[] bytes = strs[i].getBytes();
    Arrays.sort(bytes);
    strings[i] = new String(bytes);
}
//遍历新的字符串数组,将字符串相同的归类(已经归类的字符串,可以设置为null)
for (int i = 0; i < len; i++) {
    ArrayList<String> strings1 = new ArrayList<>();
    //如果为空说明已经归类
    if (strings[i] != null) {
        strings1.add(strs[i]);
        //找其他排序后相同的字符串
        for (int j = i + 1; j < len; j++) {
            if (strings[i].equals(strings[j])) {
                strings1.add(strs[j]);
                // 已归类的字符串置为空
                strings[j] = null;
            }
        }
    }
//归类的字符串组放到结果集中
	if (strings1.size() != 0)
                result.add(strings1);
        }
        return result;
    }
}

法四:哈希表解法
1.准备几个bucket,用于存放分组后的原字符串
2.根据排序后的字符串,找到所属的bucket,将原字符串放入bucket
排序后的字符串作为key,通过排序后的字符串findbucke
字符串数组遍历结束后,就完成了分组
3.将每个bucket中的子结果集放到最终的结果集中

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> mp;
        vector<vector<string>> result;//结果集
		//排序;将对应将原字符串放入bucket
        for(auto str : strs) {
            auto tempStr = str;
            sort(tempStr.begin(), tempStr.end());
            mp[tempStr].push_back(str);
        }
		//放入结果集
        for(auto elem : mp) {
            result.push_back(elem.second);
        }
        return result;
    }
};

总结:
一旦需要根据特征进行归类的,用散列表
这种分类题,思路其实可以确定下来:第一遍遍历就要分好类,第二遍取出来作为ans即可。
那么,key的选取思路才是核心。每一题都可能有每一题的特征。
对于本题而言,经过排序后,乱序的字符串可以统一分为一类

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string,vector<string>> mymap;

        for( int i=0; i<strs.size() ;i++ )
        {
            string str = strs[i];
            sort( str.begin() , str.end() );//排序
            mymap[str].push_back( strs[i] );
        }
        vector<vector<string>> ans;//结果集
        for(auto it = mymap.begin() ;it!= mymap.end() ;it++ )
        {
            ans.push_back(it->second);
        }

        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值