Algorithm trial (almost equivalent to going through ACWING again to prepare for the summer camp)

1. The longest non-repeating substring

I don’t need to say the specific meaning of this topic. I will give two algorithms here.

1) Violent search

As long as the machine is fast enough, there is nothing that search can’t solve ^ ^ (just kidding
It’s very simple, we only need to traverse the length and follow the left boundary. There should be nothing to say about this

s = input()
n = len(s)
def solve(s): # Determine whether the string is repeated, return True to represent no repetition
    charstr = set()
    for ch in s:
        if ch in charstr:
            return False
        charstr. add(ch)
    return True

res = 1
for maxl in range(2,n + 1): # string length
    for i in range(n - maxl + 1): # left boundary
        s2 = s[i : i + maxl]
        if solve(s2): res = maxl # Our maxl is incremental, no need to compare

2) Sliding window

We can clearly see that the violent search actually wastes a lot of resources. For example, if the subsequence from 1-5 has been repeated, there is no need to search for the subsequence 1-6. Therefore, the sliding window Ideas arise
We have two pointers, left and right. Right moves first. If the traversed element is not in the set, add it and lens + 1. Otherwise, we let left move, and left deletes the traversed element. Until the element pointed to by the right pointer is not in the set

The specific code is as follows

s = input()
n = len(s)
left, right, lens, maxlen = 0, 0, 0, 0
strset = set()
while right < n:
    if s[right] not in strset:
        strset. add(s[right])
        right + = 1
        lens + = 1
        maxlen = max(lens,maxlen)
    else:
        while (s[right] in strset):
            strset. remove(s[left])
            left + = 1
            lens -= 1
print(maxlen)

2. Quick row

The idea of quick sorting is very simple. It is to take a number, and then find the position where the number should be, so that the left side of the number is smaller than him, and the right side is larger than him, divide and conquer (whether this number should be changed to It doesn’t matter where he should be)

Specific code

n = int(input())
a = [int(x) for x in input(). split()]
def quicksort(a,l,r):
    if l >= r : return
    x = a[l]
    i, j = l - 1, r + 1
    while (i < j):
        i + = 1
        j -= 1
        while (a[i] < x):
            i + = 1
        while (a[j] > x):
            j -= 1
        if (i < j): a[i], a[j] = a[j], a[i]
    quicksort(a,l,j)
    quicksort(a,j + 1,r)

quicksort(a,0,n - 1)
print(' '.join(map(str,a)))

3. Merge sort

It also adopts the idea of divide and conquer, and a picture can well show the sorting of divide and conquer

Local order –> Global order

The specific code is as follows

n = int(input())
a = [int(x) for x in input(). split()]
def merge_sort(a,l,r):
    if l >= r : return
    mid = (l + r) // 2
    merge_sort(a, l, mid)
    merge_sort(a, mid + 1, r)

    temp = []
    i,j = l,mid + 1
    while (i <= mid and j <= r) :
        if a[i] < a[j]:
            temp.append(a[i])
            i + = 1
        else:
            temp.append(a[j])
            j + = 1
    
    if i <= mid:
        temp + = a[i : mid + 1]
    else:
        temp + = a[j : r + 1]
    a[l : r + 1] = temp[:]

merge_sort(a, 0, n - 1)
print(' '.join(map(str,a)))

4. Integer dichotomy

This is actually two templates, and the specific problems will be covered in detail.
The first is mid = (l + r) // 2, at this time it depends on a[mid] >=k r = mid
The second is mid = (l + r + 1) // 2, at this time, look at a[mid] <=k l = mid
Else just need to pay attention, r is always l – 1

The specific code is as follows

## Integer binary, find the range of values in the list
# The first line number n and the number to be queried
# The second line is a monotonically increasing array
# The rest of the next few lines are the number of queries
# Floating point binary is not so complicated, no + 1 -1
n ,q = map(int, input(). split())
a = [int(x) for x in input(). split()]
for i in range(q):
    l , r = 0, n - 1
    k = int(input()) # number to be queried
    # first template
    while (l < r):
        mid = (l + r) // 2
        if (a[mid] >= k):
            r = mid
        else:
            l = mid + 1

    if a[l] != k : print('-1 1')
    else:
        print(l, end = ' ') # l is equivalent to the left border
        print(r)
    # The second template
    l , r = 0, n - 1
    while (l < r):
        mid = (l + r + 1) // 2
        if (a[mid] <= k):
            l = mid
        else:
            r = mid - 1

    print(l) # This l is equivalent to the right border

5. One-dimensional prefix sum

There is nothing to say about this, just to quickly find the sum of the array from l to r
# It is very convenient to find a[l] + … + a[r], the subscript starts from 1
n ,m = map(int,input().split())
a = [0] + [int(x) for x in input(). split()]
s = [0] * (n + 1)
for i in range(1,n + 1):
s[i] = s[i – 1] + a[i]

for i in range(m): # m queries
    l, r = map(int, input(). split())
    print(s[r] - s[l - 1])

—————Now it’s too slow to react one by one, I will directly transfer to graph theory and DP, and focus on these two parts

shortest path first

6.dijkstra

This is actually quite simple, it just needs to be proficient, and I have already struggled to write it in retrospect.
The principle of dijkstra is very simple. It is to repeat n (the number of points) times each time, find a point with the shortest distance from the origin each time, and use it to update the distance from the origin to other points. I will not elaborate on the specific proof here. , the time complexity is n^2, it seems that there is a heap optimization method that can achieve a complexity of mlogn

The specific code is as follows

N,null = 510,0x3f3f3f3f


def dijkstra():
    dist[1] = 0
    
    for _ in range(n): # update n times because there are n points, each time determine one
        t = -1
        # Find the closest point to the source point in the set of points without a confirmed shortest path
        for j in range(1,n + 1):
            if (not st[j] and (t == -1 or dist[t] > dist[j])): # He is the first point or the distance between the following points and the origin is smaller than him
                t = j
        st[t] = True
        for j in range(1,n + 1): # Use t to update In fact, as long as the update is uncertain, but for the convenience of the code, all are updated together
            dist[j] = min(dist[j],dist[t] + g[t][j])
    
    if dist[n] == null : return -1
    else: return dist[n]


if __name__ == '__main__':
    g = [[null]*N for _ in range(N)] # adjacency matrix for dense graph
    dist,st = [null]*N,[False]*N # dist is used to store the distance from each point to the starting point st is used to indicate that the current point has been determined to be the shortest
    n,m = map(int,input().split())
    
    for _ in range(m):
        x,y,z = map(int,input().split())
        g[x][y] = min(g[x][y],z) # There may be multiple sides, take the smallest
    
    print(dijkstra())

7. Bellman-ford

This algorithm is to update multiple times according to the edge information, and finally update n (number of points) times to find the shortest path from 1 to n
The advantages of this algorithm are: 1. Negative edges are allowed, 2. He can limit how many edges can be passed from 1 to n at most (wait and see the question)

The specific code is as follows

N, null = 100010, 0x3f3f3f3f
import copy
def bellman_ford():
    dist[1] = 0
    for _ in range(k): # traverse k times, because there is a limit
        last = copy.deepcopy(dist) # Copy it, it is only used when the number of sides is limited, for fear of concatenation
        for j in range(m): # traversal updates all edges
            a, b, c = edges[j]
            dist[b] = min(dist[b], last[a] + c)

    if dist[n] > null / 2: print('impossibile')
    else: print(dist[n])




if __name__ == '__main__':
    n, m, k = map(int, input(). split())
    dist = [null] * N
    edges = []
    for _ in range(m):
        x, y, z = map(int, input(). split())
        edges. append((x, y, z))
    bellman_ford()

8.spfa

This algorithm is actually an improvement on bellman-ford. In the worst case, it has the same time complexity as the bellman-ford algorithm (it seems that most games will get stuck on this). He introduced an idea that only the updated point can update other points. , look at the code specifically, I don’t really like to use linked lists for storage. If there are too many points, the adjacency matrix needs to be 100000*100000, which is obviously too large. You can consider using two lists to store the connection of points and the weight of points ( If you don’t use a linked list), it should be pretty easy to implement. I’ll just use the adjacency matrix here.

The specific code is as follows

##I still don't like to use the linked list to save, I feel a little troublesome
from collections import deque


def spfa():
    dist[1] = 0
    q = deque([1])
    while q:
        t = q.popleft() # Pop the element at the beginning
        st[t] = False # he is not in the queue anymore
        for i in range(1, n + 1): # This step is actually to find the nodes connected to t, but I have no good way, I can only traverse all
            if g[t][i] == null: continue
            if dist[i] > dist[t] + g[t][i]:
                dist[i] = min(dist[i], dist[t] + g[t][i])
                if not st[i]:
                    q.append(i)
                    st[i] = True
    if dist[n] > null / 2: print('impossible')
    else: print(dist[n])




N = 510
null = 0x3f3f3f3f
dist, st = [null] * N, [False] * N
g = [[null] * N for _ in range(N)]
n, m = map(int, input(). split())
for _ in range(m):
    x, y, z = map(int, input(). split())
    g[x][y] = min(g[x][y], z)
spfa()

9. spfa judges negative ring

His way of judging the negative ring is actually very simple. As long as there is an edge that has been updated more than n times, it means that there is a negative ring, so it only needs to be changed a little under the original code. It should be noted that we need to Add all the points to the queue, because point 1 may not reach the negative ring

The specific code is as follows

##I still don't like to use the linked list to save, I feel a little troublesome
from collections import deque


def spfa():
    dist[1] = 0
    q = deque([x for x in range(1,n + 1)]) # Because it may not be possible to reach the negative ring from point 1. So you need to go through all the points and try
    while q:
        t = q.popleft() # Pop the element at the beginning
        st[t] = False # he is not in the queue anymore
        for i in range(1, n + 1): # This step is actually to find the nodes connected to t, but I have no good way, I can only traverse all
            if g[t][i] == null: continue
            if dist[i] > dist[t] + g[t][i]:
                dist[i] = min(dist[i], dist[t] + g[t][i])
                cnt[i] = cnt[t] + 1
                if cnt[i] >= n : return 'Yes'
                if not st[i]:
                    q.append(i)
                    st[i] = True
    return 'No'



if __name__ == '__main__':
    N = 510
    null = 0x3f3f3f3f
    dist, st = [null] * N, [False] * N
    cnt = [0] * N
    g = [[null] * N for _ in range(N)]
    n, m = map(int, input(). split())
    for _ in range(m):
        x, y, z = map(int, input(). split())
        g[x][y] = min(g[x][y], z)
    spfa()

10.flord

This kind of n^3 approach is quite violent. Haha, there is nothing to say about this. Three fors are enough.

The specific code is as follows

N, null= 210, 0x3f3f3f3f

def flord():
    for k in range(n + 1):
        for i in range(n + 1):
            for j in range(n + 1):
                g[i][j] = min(g[i][j], g[i][k] + g[k][j])





if __name__ == '__main__':
    n, m, k = map(int,input().split()) # k queries
    g = [[null] * N for _ in range(N)]
    for i in range(n + 1): # flord is updated on g, so do this step
        g[i][i] = 0
    for _ in range(m):
        x, y, z = map(int, input(). split())
        g[x][y] = min(g[x][y], z)
    flord()
    for _ in range(k):
        x, y = map(int, input(). split())
        if g[x][y] > null / 2: print('impossible')
        else: print(g[x][y])