主要工場の面接のためのアルゴリズムによる実際の質問

ファーウェイ

ICT光製品部門の裏側

整数の分割

ニウケ原題: Integer Split

整数は常に 2 のべき乗の合計に分割できます。例:
7 = 1+2+4
7 = 1+2+2+2
7 = 1+1+1+4
7 = 1+1+1+2 + 2
7 = 1+1+1+1+1+2
7 = 1+1+1+1+1+1+1
合計 6 つの異なる分割があります。
別の例: 4 は次のように分割できます。
4 = 4
4 = 1 + 1 + 1 + 1
4 = 2 + 2
4 = 1+1+2
f(n) を使用して、次のように n の異なる分割数を表します。 f(7)=6. プログラムを書き、n(1000000を超えない)を読み込み、f(n)%1000000000を出力する必要があります。

分析:
奇数と偶数に分けて議論します
。N が奇数の場合、他の成分はすべて偶数であるため、奇数成分 1 が含まれている必要があります。したがって、前の数値 N-1 を介して +1 によってのみ取得できます
。N が偶数の場合、奇数成分 1 を含むことも、含まないこともできます。したがって、前の数値 N-1 を +1 することによって取得することも、N//2 数値のコンポーネントを 2 で乗算して、内部のコンポーネントがすべて偶数になるようにすることによって取得することもできます。回帰式は次のとおりです:
dp
[ i ] = { dp [ i − 1 ] , i % 2 ! = 0 dp [ i − 1 ] + dp [ i / / 2 ] , i % 2 = = 0 dp[i] = \ begin{cases} dp[ i-1], & i\%2 != 0 \\ dp[i-1] + dp[i//2], & i\%2 == 0 \end{cases}d p [ i ]={ d p [ i1 ] d p [ i1 ]+d p [ i //2 ] ,私は%2 !=0私は%2==0

Python ソースコードの動的プログラミング:

number = int(input())

dp = [0] * (number + 1)
dp[1] = 1

for i in range(2, number + 1):
    if i&1 == 1:
        dp[i] = dp[i-1] % 1000000000
    else:
        dp[i] = (dp[i-1] + dp[i>>1]) % 1000000000

print(dp[-1])

Python ソースコードのバックトラック:

def demo(n):
    res = []
    path = []

    def backtracking(val, startIndex):
        if val == 0:
            res.append(path[:])
            return
        if val < 0:
            return

        i = startIndex
        while 2 ** i <= val:
            val -= 2 ** i
            path.append(2 ** i)
            backtracking(val, i)
            path.pop()
            val += 2 ** i
            i += 1
    
    backtracking(n, 0)
    return res

res = demo(7)
print(len(res))
print(res)

印刷結果:

6
[[1, 1, 1, 1, 1, 1, 1], 
[1, 1, 1, 1, 1, 2], 
[1, 1, 1, 2, 2], 
[1, 1, 1, 4], 
[1, 2, 2, 2], 
[1, 2, 4]]

アップグレードされたバージョン:
整数は、非負数の合計以下の分割メソッドに分割されます。つまり、2 の累乗への分割は制限されません。

分析:
整数分割問題に対する 4 つの解決策
問題定義:
dp[i][j]: 数値 i を j までの整数で分割できる型の数を示します。

動的移動方程式は 3 つの場合に分けられます。
1) i=j のとき、この時点での分割種の数 dp[i][j] は dp[i][j-1] の数より 1 つだけ多くなります。この場合、i は j で表すことができます。したがって、 dp[i][j]=dp[i][j-1]+1
2) i<j の場合、このときの分割数 dp[i][j] は dp[i][i] に等しい]、整数 i を i より大きい j に分割することはできないため、i より大きい j の部分は効果がないことを意味します。したがって、 dp[i][j]=dp[i][i]
3) i>j のとき、このときの分割数 dp[i][j] は 2 つの状況で構成されます。まず、分割数 j があります。この場合のタイプの数は dp[ij][j] です; 2 番目に、分割には番号 j はなく、この場合のタイプの数は dp[i][j-1] dp です[
i ] [ j ] = { dp [ i ] [ j − 1 ] + 1 、 i = = jdp [ i ] [ i ] 、 i < jdp [ i − j ] [ j ] + dp [ i ] [ j − 1 ] , i > j dp[i][j] = \begin{cases} dp[i][j-1] + 1, & i==j \\ dp[i][i], & i<j \\ dp[ij][j] + dp[i][j-1], & i>j \end{cases}d p [ i ] [ j ]= d p [ i ] [ j1 ]+1 d p [ i ] [ i ] d p [ ij ] [ j ]+d p [ i ] [ j1 ]==j<j>j

初期化:
i=1 または j=1 の場合は 1 つのケースのみ

Python ソースコードの動的プログラミング:

def dfs(n):
    dp = [[0] * (n+1) for _ in range(n+1)]
    for i in range(1, n+1): dp[i][1] = 1
    for i in range(1, n+1): dp[1][i] = 1

    for i in range(2, n+1):
        for j in range(2, n+1):
            if i == j: dp[i][j] = dp[i][j-1] + 1
            elif i < j: dp[i][j] = dp[i][i]
            else: dp[i][j] = dp[i-j][j] + dp[i][j-1]

    return dp[-1][-1]

Python ソースコードのバックトラック:

def demo(n):
    res = []
    path = []

    def backtracking(val, startIndex):
        if val == 0:
            res.append(path[:])
            return
        if val < 0:
            return

        i = startIndex
        while i <= val:
            val -= i
            path.append(i)
            backtracking(val, i)
            path.pop()
            val += i
            i += 1
    
    backtracking(n, 1)
    return res

res = demo(4)
print(len(res))
print(res)

印刷結果:

5
[[1, 1, 1, 1], 
[1, 1, 2], 
[1, 3], 
[2, 2], 
[4]]

端末BG-AIとスマートフルシナリオ側

文字列変換

Niu Ke Mianjing: Huawei-AI Engineer-Consumer BG-Mianjing Mianjing
仕上げ計画 - 第 6 回
ここに画像の説明を挿入
分析:
貪欲な思考
1) ターゲット内の各文字について、ブロックに移動して 1 つずつ一致させる
2) 一致する場合 成功した場合は、両方target と block を右に 1 ビットシフトして照合を続ける
3) 照合に失敗した場合は res+1 してブロックの先頭から照合を開始する
4) ブロック全体が照合できなかった場合、ブロックからターゲットを取得できないことを示す -1 を返します。

Python ソースコード:

def demo(target, block):
    res = 0
    i = 0
    while i < len(target):
        j = 0
        flag = True
        while j < len(block):
            if i < len(target) and target[i] == block[j]:
                i += 1
                flag = False
            j += 1
        if flag: return -1
        res += 1
    return res

target = 'abcd'  # 'aaa', 'abcd'
block = 'bcad'  # 'ad', 'bcad'
print(demo(target, block))

連続する 1 を含まない最小の文字列

0 と 1 のみを含む文字列が指定された場合、その中に連続する 1 があるかどうかを判断します。存在する場合は、この文字列より大きい連続 1 のない最小値文字列を出力します。そうでない場合は、何もしません。
例: '11011' の場合は '100000' が出力され、'10011' の場合は '10100' が出力されます。

分析:
まだまだ欲張りな思考
1) 左から右に検索し、最初の 2 つの連続する 1 を見つけます。そうでない場合は、元の文字列を直接返します。
2) 連続した位置で前の記号 (0) を 1 に変更します。次の記号はすべて 0 になります。 3
) 変換後、チェックを続けると連続1があれば、なければそのままリターン

Python ソースコード:

def demo(strInput):
    start = check(strInput)

    while start != -1:
        if start == 0:
            strInput = '1' + '0'*len(strInput)
        else:
            strInput = strInput[:start-1] + '1' + '0'*(len(strInput)-start)
        start = check(strInput)

    return strInput

def check(strInput):
    for i in range(len(strInput)-1):
        if strInput[i] == strInput[i+1] and strInput[i] == '1':
            return i
    return -1

print(demo('10011'))

コレクションのサブセット

セットが与えられた場合、そのセットのすべてのサブセットを出力し、作成するアルゴリズムの時間計算量を与えます。
例: セット [1, 2, 3] を指定すると、出力 [[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]。

LeetCode の元のタイトル: Sword Pointer Offer II 079。すべてのサブセット

分析:
バックトラック思考、現在の要素がサブセットに配置されているかどうかの 2 つの場合に分けて分析

Python ソースコード:

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        path = []

        def backtracking(cur):
            if cur == len(nums):
                res.append(path[:])
                return
            
            # 当前元素放入path中
            path.append(nums[cur])
            backtracking(cur + 1)
            path.pop()

            # 当前元素不放入path中
            backtracking(cur + 1)

        backtracking(0)
        return res

大きな数の乗算

ここに画像の説明を挿入

2 つの数値を文字列として読み取り、それらを乗算する関数を作成し、それらを文字列として返します。

ニウケ原題:NC10大数乗算

分析:
優れた解決策:
[アルゴリズム] 大数乗算問題とその効率的なアルゴリズム
カラツバ 大数乗算アルゴリズム
Python 大数乗算

Python ソースコード:

class Solution:
    def solve(self , s: str, t: str) -> str:
        # write code here
        if s == '0' or t == '0': return '0'
        res = [0] * (len(s) + len(t))
        sList = list(s)
        tList = list(t)
        sList.reverse()
        tList.reverse()
        # 将t里面的每个数字与s相乘,并保存在对应为res位数上
        for i in range(len(sList)):
            for j in range(len(tList)):
                res[i+j] += int(sList[i]) * int(tList[j])
        # 将res位数上的数字进行取余和求商运算,余数放在当前位置上,商进行进位
        for i in range(len(res)):
            temp = res[i]
            res[i] = str(temp % 10)
            if i < len(res)-1: res[i+1] = res[i+1] + temp // 10
        # 返回结果,注意移除res前面的0
        res.reverse()
        resStr = ''.join(res)
        for i in range(len(resStr)):
            if resStr[i] != '0': break
        resStr = resStr[i:]
        return resStr

最長の回文部分文字列

LeetCode の元の質問: 5. 最長の回文部分文字列

端末BG-AIとインテリジェントフルシーンの両面

スライドウィンドウ

LeetCode の元の質問: 209. 最小長の部分配列

百度

百度の自動運転側

繰り返される文字の短縮形

繰り返し文字には省略表現があります。
(1) 繰り返し部分は「(繰り返し内容)<繰り返し回数>」の形式で記録され、入れ子の略語関係が存在する場合があります。 (2) 繰り返し部分以外は次のように記録されます
。最初に文字列 str が与えられた場合
、文字列短縮表現の前の内容を復元する関数を実装してください。str は小文字、数字、()、<> のみで構成されます。
例:
str="abc(d)<2>"、短縮表現の前の文字列: "abcdd"
str="a(b(c)<2>d)<3>e"、短縮表現の前の文字列は:「abccdbccdbccde」

分析:
最初のアイデアは単純で、スタックを使用するというものです。その後、これらの特殊文字や文字を記録する方法が面接でまた難しくなりました。しばらく考えていませんでしたが、しばらくすると理解できるようになります手順は次のとおりです。 1
) 添字 i ループに従って str 内の文字をスタックにプッシュします。
2) 現在の文字が記号 ")" であるかどうかを判断します。そうである場合、現在繰り返される文字が見つかったことを意味します。ここで注意が必要なのは、")" 記号の場合、手順 3)、4)、5) を実行することです。実行後、ループ添え字 i は "<> 内の内容をスキップする必要があります。 「)」記号でない場合は、i+1して次のループに入る
3) 「<>」の後ろの記号「)」内の内容を読み取る、つまり繰り返し回数を取得する
4)シンボル「)」を前から近いものに読み込んでいきます"("シンボル、実装方法は、while ループがスタックからポップします要素をポップアウトし、ポップされた要素を新しい一時スタック temp_stack に置き、ループを停止します記号「(」がわかると、このとき現在の繰り返し内容がtemp_stackに格納されます。 5) 繰り返し回数に応じて、順番にtemp_stackの要素をスタックスタックに戻します
戻っても、temp_stack スタックの先頭からトラバースする必要があります。
6) 文字列 str 内のすべての要素がトラバースされるまで、短縮表現の前の内容がスタックに格納されます。直接 "".join(stack) を返して完了します。機能全体

Python ソースコード

def demo(strInput):
    stack = []
    i = 0
    while i < len(strInput):
        if strInput[i] == ')':
            # 读取 <> 里面的重复次数
            j = i + 2 # 跳过 < 符号
            times = ""
            while strInput[j] != '>':
                times += strInput[j]
                j += 1
            times = int(times)
            # 读取 ) 符号到最近 ( 符号里面的重复内容
            str_val = stack.pop()
            temp_stack = []
            while str_val != '(':
                temp_stack.append(str_val)
                str_val = stack.pop()
            # 根据重复次数依次将temp_stack里面的元素再放回stack中
            for i in range(times):
                for v in range(len(temp_stack)-1, -1, -1):
                    stack.append(temp_stack[v])
            # 下标 i 跳位,跳过 <> 符号以及里面的重复次数
            i = j + 1
        else:
            stack.append(strInput[i])
            i += 1
    return "".join(stack)

有効な括弧

上記の質問は面接中に行われなかったので、面接官はより和気あいあいとして、別の簡単な質問をしました. この質問は LeetCode のオリジナルの質問です. 幸運なことに、私はそれを実行し、一気に LeetCode を書きました: 20. 効果的な括弧付き
コード
考え方: 20. 有効な括弧

'('、')'、'{'、'}'、'['、']' のみで構成される文字列 s が指定された場合、その文字列が有効かどうかを判断します。
有効な文字列は次の条件を満たしている必要があります。
1) 左括弧は同じ種類の右括弧で閉じられている必要があります。
2) 開き括弧は正しい順序で閉じなければなりません。
例:
入力: s = "(]"、出力: false
入力: s = "()[]{}"、出力: true
入力: s = "([)]"、出力: false

分析:
1) str の長さが偶数でない場合は、不一致があることを意味し、直接 False を返すことができます。 2)
str を順番にスタックに入れます。
3) 現在の文字が偶数であるかどうかを判断します。 ")"、"]"、" }" の場合、スタック内の要素をポップアウトし、ポップされた要素が現在の文字と一致するかどうかを判断し、一致しない場合は直接 False を返します。 4) 最後に返すときは、スタック
が空かどうかを判断するだけでよく、trueの場合はTrueを返します。

Python ソースコード

def demo(strInput):
    # 快速判断
    if len(strInput) % 2 != 0: return False
    stack = []
    str_pairs = {
    
    ')': '(', ']': '[', '}': '{'}
    for v in strInput:
        if v in str_pairs:
            # 细节问题:应该要先判断stack是否为空
            # 不然当第一个元素就为')'、']'或'}'时,程序会报错
            # 面试时候这点没有考虑到,直接判断了pop出来的元素
            if len(stack) == 0 or stack[-1] != str_pairs[v]:
                return False
            else:
                stack.pop()
        else:
            stack.append(v)
    # 细节问题:我面试的时候写的是
    # if len(stack) == 0: return True
    # else: return False
    # 这里被面试官提醒了,说这个返回太臃肿了,可以不可以简化一下
    # 结果我没说出来
    # 面试官直接说了:
    # return len(stack) == 0
    # return not stack 可能是最简洁的了,又学习了
    return not stack

バイト

バイトスマートクリエーション

ソートされた配列の平方

簡単な質問を直接思いついたのですが、以前PUAをやりすぎて頭が真っ白になり、この質問からACが出なかったので非常に不快な思いをしました LeetCode: 977. 順序付けされた配列の2乗
コードに関するランダムな考え:
977 . 順序付けされた配列の 2 乗は、
非反復要素を返す必要があるという点で LeetCode の元の質問とは少し異なります。

非降順でソートされた整数 nums の配列を指定すると、同じく非降順でソートされた各数値の 2 乗で構成される新しい配列を返します。
例:
入力: nums = [-5,-4,-1,0,1,3,5,5]、出力: [0,1,9,16,25]
入力: nums = [-4,-1 ,0,3,10]、出力: [0,1,9,16,100]

分析
1) 最初と最後の double ポインター、つまり、入力配列の最初の要素と最後の要素をそれぞれ指す、left と right ポインターを定義します。 2) left と right が指す要素の 2 乗値を決定します
。 , そして、大きい方のポインタの 2 乗値を入力します 結果の終わりを入力してください。それが終わりでなければならないことに注意してください。そのため、挿入を使用する必要があります。インタビュー中に直接追加を使用しましたが、これがこの質問に失敗する直接の原因でした。頭が真っ白で、そんな簡単な質問が見つからなかったので、ポインタを動かすと、左なら右に、右なら左に移動します

Python ソースコード

def demo(nums):
    result = []
    left, right = 0, len(nums)-1
    pre = -1 # 记录添加到result里面的最新元素,用来避免重复元素的添加
    while left <= right: # 一定要有等于,不然会漏掉一个元素
        left_square = nums[left] * nums[left]
        right_square = nums[right] * nums[right]
        if left_square >= right_square: # 取出那个较大的平方值
            if left_square != pre:
                # 注意一定是insert,而不是append!!!
                # 插入到数组的末端
                result.insert(0, left_square)
                pre = left_square
            left += 1
        else:
            if right_square != pre:
                result.insert(0, right_square)
                pre = right_square
            right -= 1
    return result

TnS コンテンツ セキュリティ側

携帯電話の画面ロック解除方法

入力: m=2、n=2、s=2。m、n は行列のサイズ (m 行 n 列) を表し、s はロック解除ステップの数を表します。つまり、出力にいくつかの点が描画されます: '1->2'、'1->3
' 、「1->4」、「2->1」、「2->3」、「2->4」、「3->1」、「3->2」、「3->4」、 「4->1」、「4->2」、「4->3」

m=2、n=2 の場合、行列は次のようになります:
[[1, 2],
[3, 4]]
m=3、n=3 の場合、行列は次のようになります:
[[1, 2, 3],
[4] 、5、6]、
[7、8、9]]

分析:
この質問のアイデアは難しくありません, バックトラッキングの使用を直接考えることができます.
難しいのはコードの能力を調べることです.
類似の質問
LeetCode: 79. 単語検索

Python ソースコード

def demo(m, n, s):
    res = []
    path = []

    # 生成矩阵
    # m=2, n=2: [[1,2], [3,4]]
    # m=3,n=3: [[1,2,3], [4,5,6], [7,8,9]]
    board = [[0] * n for _ in range(m)]
    start = 1
    for i in range(m):
        for j in range(n):
            board[i][j] = start
            start += 1

    def backtracking(board, i, j):
        # 如果路径长度等于s,说明该路径已经完成,直接返回True
        if len(path) == s:
            res.append('->'.join(path))
            return True
        # 如果回溯的下标不满足矩阵的范围,就返回False
        if not (0<=i and i<m and 0<=j and j<n): return False
        # 用-1表示这个位置已经经过,就返回False
        if board[i][j] == -1: return False

        # 在path中添加当前元素
        path.append(str(board[i][j]))
        temp = board[i][j]
        # 将当前元素置为-1,表示当前元素已经经过
        board[i][j] = -1
        flag = False
        for si in range(-1, 2):
            for sj in range(-1, 2):
                if si == 0 and sj == 0: continue
                if backtracking(board, i+si, j+sj):
                    flag = True
                    break
            if flag: break
        # 弹出当前元素
        path.pop()
        # 重新归位当前元素
        board[i][j] = temp

    # 遍历所有元素,定义路径的起点
    for i in range(m):
        for j in range(n):
            backtracking(board, i, j)

    return res

print(demo(2, 2, 2))

TnS コンテンツ セキュリティの 2 つの側面

問題を見つける

LeetCode 原題: 33. 検索回転ソート配列
81. 検索回転ソート配列 II
ここに画像の説明を挿入
分析:
中心となるアイデア:
配列を 2 つに分割し、一方は順序どおりである必要があり、もう一方は順序どおりまたは部分的に順序があり得る。
このとき、順序付けされた部分はバイナリ方式で検索されます。順序付けされていない部分は 2 つの部分に分割され、そのうちの 1 つは順序付けする必要があり、もう 1 つは順序付けまたは無秩序にすることができます。このループのように、
1) 中央の値と現在の左の値を比較します。中央の値が現在の左の値以上であれば、中央の値の左側が正しいことを意味します。次に、対象の値が左側の順序配列にあるかどうかを判断し、存在する場合は右側の範囲を絞り、そうでない場合は左側の範囲を絞ります
次に、ターゲット値が右側の順序付けられた配列にあるかどうかを判断し、存在する場合は左側の範囲を縮小し、そうでない場合は右側の範囲を縮小し
ます

Python ソースコード:

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums)-1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target:
                return mid

            if nums[mid] >= nums[left]:
                if nums[left] <= target <= nums[mid]:
                    right = mid - 1
                else:
                    left = mid + 1
            else:
                if nums[mid] <= target <= nums[right]:
                    left = mid + 1
                else:
                    right = mid - 1

        return -1

TnS コンテンツ セキュリティの 3 つの側面

スライドウィンドウ

文字列を指定して、繰り返し文字を含まない最長の文字列を検索します。
例: input 'abbbcdefff'、output 'bcdef'

Python ソースコード:

def check(alphaDict):
    for key, val in alphaDict.items():
        if val > 1: return False
    return True

def demo(strInput):
    maxLen = 0
    res = ''
    left = right = 0
    alphaDict = dict()
    while right < len(strInput):
        if strInput[right] not in alphaDict: alphaDict[strInput[right]] = 1
        else: alphaDict[strInput[right]] += 1
        while not check(alphaDict):
            alphaDict[strInput[left]] -= 1
            left += 1
        tmpLen = right - left + 1
        if tmpLen > maxLen:
            maxLen = tmpLen
            res = strInput[left:right+1]
        right += 1
    return res

print(demo('abbbcdefff'))

NIO

自動運転の二つの側面

文字列変換

Huawei BG への質問

早い労働者

コンピュータビジョン側

IoUの計算

美団

北斗 - 無人配送車両側

ローテーションリスト

LeetCode の元の質問: 61. 回転リンク リスト

スライディングウィンドウの最大値

LeetCode 元のタイトル: 239. 最大スライディング ウィンドウ

おすすめ

転載: blog.csdn.net/qq_33757398/article/details/125814937