Acmerはコードを引き裂くことができなければなりません

ボードを持参せずにSTLを使用する必要がある場合、コードは手動でのみ引き裂くことができます。

さらに、手書きの単語は、知識ポイントのより深い理解とゲーム中のより速い速度を示します。(私は引退しましたが)

ただし、筆記面接ではSTLを使用できない可能性が高いため、今回は基本的なアルゴリズム(メモリ)の理解度をテストします。

一般的に言えば、アルゴリズムの競争の高度なガイドに表示されるコードは引き裂かれる必要があります

基本的なアルゴリズム

二分法

  • $ \ geq x $の最小値単調増加シーケンス\(a \)で検索
  while(l<r){
      int mid = (l+r)>>1;/*右移运算 相当于除2并且向下取整*/
      if(a[mid]>=x) r=mid;
      else l=mid+1;
  }
  return a[l];
  • 単調に増加するシーケンスで\(A \)ルックアップ\(\当量X \)の数最大の一つ
  while(l<r){
      int mid = (l+r+1)>>1;
      if(a[mid]<=x) l=mid;
      else r=mid-1;
  }
  return a[l];

最小の回答の回答が選択されている場合は、\(l = L、r = R + 1 \)を初期化します。回答が\(R + 1 \)の場合は見つかりません

最大の回答を満たすものを選択した場合は、\(l = L-1、r = R \)を初期化します。回答が\(L-1 \)の場合、それは見つかりません

upper_bound()やと同様にlower_bound()存在しない場合は最後の添え字+1を返し、ベクトルはend()

並べ替え

バブルソート

隣接比較、右シフト大

void bubble_sort() {
	for (int i = n - 1; i > 0; --i) {
		bool flag = false;
		for (int j = 0; j + 1 <= i; ++j) 
			if (a[j] > a[j + 1]) { 
				std::swap(a[j], a[j + 1]);
				flag = true;
			}
		if (!flag) return ; 
	}
	return ;
}

並べ替えを選択

最大の右シフトであるキーを選択します

void selection_sort() {
	for (int i = 0; i < n; ++i) {
		for (int j = i + 1; j < n; ++j) {
			if(a[i] > a[j]) 
				std::swap(a[i], a[j]);
		}
	}
}

ソートを挿入

手前の場合は、挿入したい位置に挿入するキーを選択

int i,j,key;
for(i = 2;i <= n;++i){
    key = a[i];
    j = i - 1;
    while(j > 0 && a[j] > key){
        a[j+1] = a[j];
        j--;
    }
    a[j] = key;
}

クイックソート

選択されたものの間の数、左はそれより小さく、右はそれより大きい必要があり、次に再帰的な分割と征服

void quick_sort(int l, int r) {
	if (l >=r ) return ;
	int i = l - 1, j = r + 1, x = a[(l + r) >> 1];
	while (i < j) {
		do j--; while (a[j] > x);
		do i++; while (a[i] < x);
		if (i < j) std::swap(a[i], a[j]);
		else quick_sort(l, j), quick_sort(j + 1, r);
	}
}

マージソート

int merge_sort(int l, int r) {
	if (l >= r) return 0;
	int mid = (l + r) >> 1, res = 0;
 
	res += merge_sort(l, mid);
	res += merge_sort(mid + 1, r);
 
	int i = l, j = mid + 1, cnt = 0;
	while (i <= mid && j <= r) 
		if (a[i] <= a[j]) 
			b[cnt++] = a[i++];
		else {
			res += mid - i + 1;
			b[cnt++] = a[j++];
		}
 
	while (i <= mid) b[cnt++] = a[i++];
	while (j <= r) b[cnt++] = a[j++];
 
	for (int i = l, j = 0; j < cnt; ++i, ++j) a[i] = b[j];
	return res;
}

ヒープソート

大きなルートヒープの実装:xノードは子ノードより大きくなければなりません

struct heap{
    int *A;
    int length;
    heap(int n){
        length = n;
        A = new int[n+5];
        for(int i=1;i<=n;++i){
            cin>>A[i];
        }
    }
    void Max_Heapify(int pos){
        int l = pos*2;
        int r = pos*2+1;
        int largest;
        if(l <= length && A[l] > A[pos]){
            largest = l;
        }else{
            largest = pos;
        }
        if(r <= length && A[r] > A[largest]){
            largest = r;
        }
        if(largest != pos){
            swap(A[pos],A[largest]);
            Max_Heapify(largest);
        }
    }
    void build_max_heap(){
        for(int i=length/2;i>=1;--i){
            Max_Heapify(i);
        }
    }
    void heapSort(){
        build_max_heap();
        int n = length;
        for(int i=length;i>=2;--i){
            swap(A[1],A[i]);
            length--;
            Max_Heapify(1);
        }
        for(int i=1;i<=n;++i){
            cout<<A[i]<<" ";
        }
    }
};

ハッシュ

ただ式を覚えておいてください:

\ [H(T)= H(S + T)-H(S)* p ^ {Length(T)} \]

\(H(x)\):文字列のハッシュ値\(x \)

\(p \):16進数

\(p ^ x \)\(p \)ベースの下で\(x \)ビットだけ左にシフトされた値を意味します

コードが前処理されている限り\(f(x)\):プレフィックスハッシュ値\(p(x)\)\(p ^ x \)

多数

ほとんどのインタビュアーは、オブジェクト指向になりたいと言いましたか?読みやすいですか?

大量の追加を維持するために3つのスタックを使用します(インタビュアーは読みやすい方法だと言っています)

#include<bits/stdc++.h>
using namespace std;
class bigNum{
private:
    stack<int>num1,num2,sum;
public:
    char *Num;
    bigNum(char *s){
        Num = s;
    }
    bigNum* add(bigNum* &right){
        for(int i=0;Num[i];++i){
            num1.push(Num[i] - '0');
        }
        for(int i=0;right->Num[i];++i){
            num2.push(right->Num[i] - '0');
        }
        int flag = 0,tmp,x,y;
        while(!num1.empty() || !num2.empty()){
            x = num1.empty() == 1 ? 0:num1.top();
            y = num2.empty() == 1 ? 0:num2.top();
            tmp = x + y + flag;
            flag = tmp/10;
            sum.push(tmp%10);
            if(!num1.empty()) num1.pop();
            if(!num2.empty()) num2.pop();
        }
        if(flag){
            sum.push(flag);
        }
        char *ans = new char[sum.size()+1];
        int pos = 0;
        while(!sum.empty()){
            ans[pos++] = sum.top() + '0';
            sum.pop();
        }
        return new bigNum(ans);
    }
    void printNum(){
        cout<<Num<<endl;
    }
};
char a[150],b[150];
int main(){
    cin>>a;
    cin>>b;
    bigNum *left = new bigNum(a);
    bigNum *right = new bigNum(b);
    bigNum *sum = left ->add(right);
    sum ->printNum();
    return 0;
}

文字列

辞書ツリー

void ins(char *str){
    int len=strlen(str);
    int p=0;
    for(int i=0; i<len; i++){
        int ch=str[i]-'a';
        if(!tire[p][ch])
            tire[p][ch]=++ant;
        p=tire[p][ch];
    }
    cnt[p]++;
}
int srh(char *str){
    int ans=0;
    int len=strlen(str);
    int p=0;
    for(int i=0; i<len; i++){
        int ch=str[i]-'a';
        p=tire[p][ch];
        if(!p) return ans;
        ans+=cnt[p];
    }
    return ans;
}

実際、ポインターを書く方法はありますが、インタビュアーはポインターしか理解できないかもしれません

const int maxn = 26;
struct tireNode{
    tireNode* next[maxn];
    bool endpos;
    tire(){
        endpos = 0;
    }
};
struct tire{
    tireNode *root;
    tire(){
        root = new tireNode();
    }
    void Insert(char *str){
        tireNode *now = root;
        while(*str != '\0'){
            int pos = *str - 'a';
            if(now->next[pos] == nullptr){
                now -> next[pos] = new tireNode();
            }
            now = now -> next[pos];
            str++;
        }
        now -> endpos = 1;
    }
    bool Find(char *str){
        tireNode *now = root;
        while(*str != '\0'){
            int pos = *str - 'a';
            if(now -> next[pos] == nullptr){
                return 0;
            }
            now = now -> next[pos];
            str++;
        }
        return now -> endpos;
    }
};

KMP

\(次[i] \):文字列\([0 ... i] \)の接頭辞と接尾辞の最大一致位置を示します。接尾辞も接尾辞もそれ自体を含めることはできません

したがって、\(Next [0] = -1 \)です。これは、最初の文字がこの最大一致位置になってはならないためです。

したがって\(I \)、1から始まる(J \)\から始まる-1

ループの過程で維持されるループ不変式によれば、$ Next [i] \(最終的には\と等しくなければなりません) j \(つまり、\) [0 ... j] \(\で最長でなければなりません) i $は、末尾のサフィックスがパターン文字列のプレフィックスと一致する文字列です。

void get_Next(char *p){
    Next[0] = -1;
    int i = 1,j = -1;
    while(p[i] != '\0'){
        while(j != -1 && p[i] != p[j+1]) j = Next[j];
        if(p[i] == p[j+1])++j;
        Next[i++] = j;
    }
}
int kmp(char *s,char *p){
    int i = 0,j = -1;
    int ans = 0;
    while(s[i] != '\0'){
        while(j != -1 && ( p[j+1] == '\0' || s[i] != p[j+1]))j = Next[j];
        if(s[i] == p[j+1])++j;
        f[i++] = j;
        if(p[j+1] == '\0'){
           //视情况
        }
    }
    return ans;
}

ACオートマトン

重要な点は、fafailシーケンスからない最初の\(tr [u] [i] \)を見つけることです。時間を節約するために、パス圧縮が使用されます。

namespace AC {
    int tr[N][26], tot;
    int e[N], fail[N];

    void insert(char *s) {
        int u = 0;
        for (int i = 1; s[i]; ++i) {
            if (!tr[u][s[i] - 'a']) tr[u][s[i] - 'a'] = ++tot;
            u = tr[u][s[i] - 'a'];
        }
        e[u]++;
    }

    queue<int> q;

    void build() {
        for (int i = 0; i < 26; ++i) {
            if (tr[0][i]) {
                q.push(tr[0][i]);
            }
        }
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = 0; i < 26; ++i) {
                if (tr[u][i]) {
                    fail[tr[u][i]] = tr[fail[u]][i];
                    q.push(tr[u][i]);
                } else {
                    tr[u][i] = tr[fail[u]][i];
                }
            }
        }
    }

    int query(char *t) {
        int u = 0, res = 0;
        for (int i = 1; t[i]; i++) {
            u = tr[u][t[i] - 'a'];  
            for (int j = u; j && e[j] != -1; j = fail[j]) {
                res += e[j], e[j] = -1;
            }
        }
        return res;
    }
}

数学

線形ふるい

採用プロパティ:素数ではない数はすべて最小の素因数を持つ

void get_prime(){
    int pos = 0;
    for(int i=2;i<N;++i){
        if(!check[i]) {
            Prime[pos++]=i;
        }
        for(j=0;j < pos && i*Prime[j] < N;++j){
            check[i * Prime[j]] = true;
            if(i % Prime[j] == 0) {
                break;
            }
        }
    }
}

おおよその数

正の除数セットを取得する複数の方法

for(int i = 1;i <= n;++i){
    for(int j = 1;j <= n/i;++j){
        factor[i*j].push_back(i);
    }
}

単一の数の除数:除算を試してください

for(int i = 1;i * i <= n;++i){
    if(n % i == 0){
        factor[++m] = i;
        if(i != n/i) factor[++m] = n/i;
    }
}

データ構造

バランスツリー

少なくともそれは反転します。ルートノードでない場合は、Rotate

	inline void Rotate(int x) {
        int y = s[x].fa, z = s[y].fa, chk = get(x);

        //y与x的子节点相连
        s[y].ch[chk] = s[x].ch[chk ^ 1];
        s[s[x].ch[chk ^ 1]].fa = y;

        //x与y父子相连
        s[x].ch[chk ^ 1] = y;
        s[y].fa = x;

        // x与y的原来的父亲z相连
        s[x].fa = z;
        if(z) s[z].ch[y == s[z].ch[1]] = x;

        //只有x和y的sz变化了
        maintain(y);
        maintain(x);
    }

ツリー配列

int ask(int x){
    int ans = 0;
    for(;x;x -= x & -x) ans += c[x];
    return ans;
}
void add(int x,int y){
    for(;x <= N;x += x & -x) c[x] += y;
}

ラインツリー

struct node{
	int l,r;
	int sum,add;//和,延迟标记
}t[maxn << 2];
void build(int p,int l,int r){//后序遍历
	t[p].l = l,t[p].r;
	if(l == r) {t[p].sum = s[l];return;}
	int mid = (l+r) >> 1;
	build(p << 1,l,mid);
	build(p << 1 |1,mid + 1,r);
	t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;//更新来自子结点的答案
}
void spread(int p){
	if(add(p)){//如果有标记
		t[p << 1].sum = t[p].add * (t[p << 1].r - t[p << 1].l + 1);
		t[p << 1 | 1].sum = t[p].add * (t[p << 1 | 1].r - t[p << 1 | 1].l + 1);
		t[p << 1].add += t[p].add;
        t[p << 1 | 1].add += t[p].add;
        t[p].add;
	}
}
void change(int p,int l,int r,int d){
    /*if(t[p].l == t[p].r){
    	t[p].sum += d;
    }如果不用延迟标记
    */
	if(l <= t[p].l && r >= t[p].r){
		t[p].sum += d * (t[p].r - t[p].l + 1);
        t[p].add += d;
        return;
	}
	spread(p);
	int mid = t[p].l + t[p].r >> 1;
	if(l <= mid) change(p << 1,l,r,d);
	if(r > mid) change(p << 1 | 1,l,r,d);
    t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;//更新来自子结点的答案
}
long long ask(int p,int l,int r){
    if(l <= t[p].l && r >= t[p].r){
        return t[p].sum;
    }
    long long ans = 0;
    int mid = (t[p].l + t[p].r) >> 1;
    if(l <= mid) ans += ask(p << 1,l,r);
   	if(r > mid) ans += ask(p << 1|1,l,r);
    return ans;
}

グラフ理論

最短dij

  1. 初期化d [1] = 0、残りのノードは正の無限大です
  2. 最小の\(d [x] \)\(x \)とマークされたノード\(x \)を持つマークされていないノードを検索します
  3. ノードのすべての出力エッジをスキャンます\(x \)\((x、y、z)\)\(d [y]> d [x] + z \)の場合、\(d [x] + z \)更新\(y \)
memset(d,inf,sizeof(d));
d[1] = 0;
q.push(make_pair(0,1));
while(!q.empty()){
 	int x = q.top().second;
    q.pop();
    if(v[x]) continue;
    v[x] = 1;
    for(int i=head[x];i;i=e[i].next){
        int y = e[i].to;
        int z = e[i].val;
        if(d[y] > d[x] + z){
            d[y] = d[x] + z;
            q.push(make_pair(-d[y],y));
        }
    }
}

最小全域木

  1. セットを確立してチェックし、各ポイントがセットを構成します
  2. すべてのエッジを重みの小さいものから大きいものへと並べ替え、各エッジを順番にスキャンします\((x、y、z)\)
  3. \(X、Y \) このエッジを無視し、同じセット(通信)に属します
  4. それ以外の場合は、\(x、y \)あるセットをマージし、答えに\(z \)追加します
int get(int x){
    if (x == fa[x]) return x;
    return fa[x] = get(fa[x]);
}
void solve(){
    for(int i=1;i<=n;++i) fa[i] = i;//1
    sort(e + 1,e + 1 + m,cmp);//2
    int ans = 0;
    for(int i = 1;i <= m;++i){
    	int x = get(e[i].x);
        int y = get(e[i].y);
        if(x != y){
            fa[x] = y;
            ans += e[i].z;
        }
    }
}

ハンガリーのアルゴリズム

bool dfs(int x){
    for(int i=head[x];i;i=e[i].next) {
        if(!vis[y = e[i].to]){
            vis[y] = 1;
            if(!match[y] || dfs(match[y])) {
                match[y] = x;
                return true;
            }
        }
    }
}
for(int i = 1;i <= n; ++i){
    memset(vis,0,sizeof(vis));
    if (dfs(i)) {ans++;}
}

おすすめ

転載: www.cnblogs.com/smallocean/p/12681559.html