难度:中等
题目要求:
给定两个整数,被除数dividend
除数divisor
。将两数相除,要求不使用乘法、除法和取余运算。
整数除法应该向零截断,也就是截去(truncate
)其小数部分。例如,8.345
将被截断为8
,-2.7335
将被截断至-2
。
返回被除数dividend
除以除数divisor
的商。
示例1:
输入:dividend = 10, divisor = 3
输出:3
示例2:
输入:dividend = 7, divisor = -3
输出:-2
题解
一开始想不到怎么做,看了别人的代码才了解到需要用到位运算:
首先,判断以下三种情况:
- 如果 dividend = 0,那么直接返回0
- 如果 divisor = 1,那么直接返回被除数本身
- 如果 dividend = − 2 31 -2^{31} −231 并且 divisor = -1,那么商是 2 31 2^{31} 231,超出有符号整数范围,所以返回 2 31 − 1 2^{31} - 1 231−1。
其余的情况,首先判断符号的正负,之后将被除数和除数都变成负数,计算商的绝对值。因为不能够使用乘除,所以要用位运算代替
首先将除数左移,使得在除数的绝对值不超过被除数的绝对值的情况下,将除数的绝对值最大化。假设除数左移了 k 位之后变成currDivisor
,则currDivisor
= divisor x 2 k 2^{k} 2k,currDivisor
除以divisor
的商是 2 k 2^k 2k,将dividend
的值减去currDivisor
,然后对剩余的dividend
继续计算商
当
dividend
的值减去currDivisor
之后,如果dividend
的绝对值小于currDivisor
的绝对值,就需要将currDivisor
右移,直到dividend
的绝对值大于currDivisor
的绝对值
重复计算商的操作,计算两数相除的结果,当
dividend
的绝对值小于divisor
的绝对值时,商的剩余部分是0
,到这里,计算结束,得到两数相除的结果的绝对值,之后根据商的正负来的到最终结果并返回
想法代码
public static int Divide(int dividend, int divisor)
{
if (dividend == 0)
{
return 0;
}
if (divisor == 1)
{
return dividend;
}
if (dividend == int.MinValue && divisor == -1)
{
return int.MaxValue;
}
bool negative = dividend < 0 ^ divisor < 0;
dividend = dividend != int.MinValue ? -Math.Abs(dividend) : dividend;
divisor = divisor != int.MinValue ? -Math.Abs(divisor) : divisor;
int quotient = 0;
int currDiviser = divisor, factor = 1;
while (dividend >> 1 < currDiviser)
{
currDiviser <<= 1;
factor <<= 1;
}
while (dividend <= divisor)
{
while (dividend > currDiviser)
{
currDiviser >>= 1;
factor >>= 1;
}
quotient += factor;
dividend -= currDiviser;
}
if (negative)
{
quotient = -quotient;
}
return quotient;
}
难度:困难
题目要求:
给定一个字符串s
和一个字符串数组words
。 words
中所有字符串长度相同。
s
的串联子串是指一个包含words
中所有字符串以任意顺序排列连接起来的子串。
返回所有串联子串在s
中的开始索引。可以以任意顺序返回答案。
示例1:
输入:s = “barfoothefoobarman”, words = [“foo”,“bar”]
输出:[0,9]
示例2:
输入:s = “wordgoodgoodgoodbestword”, words = [“word”,“good”,“best”,“word”]
输出:[]
示例3:
输入:s = “barfoofoobarthefoobarman”, words = [“bar”,“foo”,“the”]
输出:[6,9,12]
题解:
采用滑动窗口法,用
sLength
表示字符串s
的长度,用wordLength
表示数组words
中的每个单词的长度,用wordCount
表示数组words
的长度即数组中的单词个数,对于由数组words
中的单词串联形成的子串,其长度为wordLenth X wordCount,因此可以使用定长滑动窗口寻找串联所有单词的子串,滑动窗口的大小为 w i d o w s = w o r d L e n g t h ∗ w o r d C o u n t widows = wordLength * wordCount widows=wordLength∗wordCount
对于降低时间复杂度,应将滑动窗口看成
wordCount
个单词,每个单词的长度是wordLength
。每次将滑动窗口向右移动wordLength
位,则有一个单词移出滑动窗口,有一个单词移入滑动窗口,滑动窗口中的其余单词出现的次数不变
当 i > = w o r d L e n g t h i >= wordLength i>=wordLength时,以下标
i
作为开始的滑动窗口都可以通过以下标 i − w o r d L e n g t h i - wordLength i−wordLength作为开始的窗口滑动依次得到,所以,只需要考虑首个滑动窗口的开始下标位于范围 [ 0 , w o r d L e n g t h − 1 ] [0,wordLength - 1] [0,wordLength−1]中的情况。由于范围 [ 0 , w o r d L e n g t h − 1 ] [0,wordLength - 1] [0,wordLength−1]中的每个下标都可能做为首个滑动窗口的下标,因此需要分别考虑wordLength
个开始下标
当 0 < = i < w o r d L e n g t h 0 <= i < wordLength 0<=i<wordLength时,以下标
i
作为开始的滑动窗口,其下标范围是 [ i , i + w i n d o w − 1 ] [i,i + window - 1] [i,i+window−1],如果 i + w i n d o w − 1 > s L e n g t h i + window - 1 > sLength i+window−1>sLength那么就不存在以下标i
作为开始的滑动窗口
一个滑动窗口中的子串是串联所有单词的子串,等价于每个单词在滑动窗口中的出现次数和在数组words中的出现次数相等,因此需要使用哈希表记录每个单词在滑动窗口中的出现次数和在数组
words
中的出现次数之差,出现次数之差等于0
表示出现次数相等
对于 0 < = i < w o r d L e n g t h 0 <= i < wordLength 0<=i<wordLength的每个下标
i
,执行以下操作:
- 创建哈希表记录每个单词在滑动窗口中的出现次数和在数组
words
中的出现次数之差。将数组words
中的每个单词在哈希表中的出现次数减1
- 用 [ s t a r t , e n d ] [start,end] [start,end]表示滑动窗口的下标范围,初始时start = i,end = i + window - 1。对于下标 [ i , i + w i n d o w − 1 ] [i,i + window - 1] [i,i+window−1]的滑动窗口,将其中的每个单词的出现次数加
1
,如果哈希表中的每个单词的出现次数之差都是0
,则将下标i
添加到答案中- 当 e n d + w o r d L e n g t h + 1 < s L e n g t h end + wordLength + 1 < sLength end+wordLength+1<sLength时,将
start
和end
各向右移动wordLength
位,使用移出滑动窗口的单词和移入滑动窗口的单词更新哈希表。如果哈希表中的每个单词出现次数之差都是0
,则将下标start
添加到答案中
遍历所有下标
i
之后,就可以得到每个串联子串的开始下标
想法代码
public static IList FindSubstring(string s, string[] words)
{
IList substrings = new List();
int sLength = s.Length, wordLength = words[0].Length, wordCount = words.Length;
int window = wordLength * wordCount;
for (int i = 0; i < wordLength && i + window <= sLength; i++)
{
IDictionary counts = new Dictionary();
foreach (string word in words)
{
counts.TryAdd(word, 0);
counts[word]--;
}
int start = i, end = i + window - 1;
for (int j = start; j <= end; j += wordLength)
{
string word = s.Substring(j, wordLength);
counts.TryAdd(word, 0);
counts[word]++;
if (counts[word] == 0)
{
counts.Remove(word);
}
}
if (counts.Count == 0)
{
substrings.Add(start);
}
while (end + wordLength + 1 <= sLength)
{
start += wordLength;
end += wordLength;
string prev = s.Substring(start - wordLength, wordLength);
string next = s.Substring(end - wordLength + 1, wordLength);
counts.TryAdd(prev, 0);
counts[prev]--;
if (counts[prev] == 0)
{
counts.Remove(prev);
}
counts.TryAdd(next, 0);
counts[next]++;
if (counts[next] == 0)
{
counts.Remove(next);
}
if (counts.Count == 0)
{
substrings.Add(start);
}
}
}
return substrings;
}