【优选算法】滑动窗口——leetcode——串联所有单词的⼦串(hard)

news2024/11/16 2:19:56

目录

1.题目 

2,算法原理

​编辑 1.哈希表

2.left和right指针的移动

3.滑动窗口的执行次数

3.代码实现 

1.C++代码

2.C语言代码 

4.C++知识点 

1. std::vector

2. std::string

3. std::unordered_map

4. 迭代器

5. 范围循环 (range-based for loop)

6. 动态内存管理

7. 面向对象编程(OOP)

总结


 

ce6fbd68767d465bbe94b775b8b811db.png

731bd47804784fa2897220a90a387b28.gif

专栏:优选算法

1.题目 

30. 串联所有单词的子串 

给定一个字符串 s 和一个字符串数组 words words 中所有字符串 长度相同

 s 中的 串联子串 是指一个包含  words 中所有字符串以任意顺序排列连接起来的子串。

  • 例如,如果 words = ["ab","cd","ef"], 那么 "abcdef", "abefcd""cdabef", "cdefab""efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。

返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。

提示:

  • 1 <= s.length <= 104
  • 1 <= words.length <= 5000
  • 1 <= words[i].length <= 30
  • words[i] 和 s 由小写英文字母组成

 

2,算法原理

类比438.找到字符串中所有字母异位词

如果我们把每⼀个单词看成⼀个⼀个字⺟,问题就变成了找到「字符串中所有的字⺟异位词」。⽆
⾮就是之前处理的对象是⼀个⼀个的字符,我们这⾥处理的对象是⼀个⼀个的单词。

具体思路见:【优选算法】滑动窗口——leetcode——438.找到字符串中所有字母异位词-CSDN博客

 1.哈希表

hash<string,int>

string——>一个一个的字符

int——>这个字符串出现的次数

2.left和right指针的移动

移动的步长是每个单词的长度——len

3.滑动窗口的执行次数

len次

3.代码实现 

1.C++代码

时间复杂度 O(n)

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ret;
        unordered_map<string, int> hash1; // 保存 words ⾥⾯所有单词的频次
        for (auto& s : words)
            hash1[s]++;
        int len = words[0].size(), m = words.size();
        for (int i = 0; i < len; i++) // 执⾏ len 次
        {
            unordered_map<string, int> hash2; // 维护窗⼝内单词的频次
            for (int left = i, right = i, count = 0; right + len <= s.size();
                 right += len) {
                // 进窗⼝ + 维护 count
                string in = s.substr(right, len);
                hash2[in]++;
                if (hash1.count(in) && hash2[in] <= hash1[in])
                    count++;
                // 判断
                if (right - left + 1 > len * m) {
                    // 出窗⼝ + 维护 count
                    string out = s.substr(left, len);
                    if (hash1.count(out) && hash2[out] <= hash1[out])
                        count--;
                    hash2[out]--;
                    left += len;
                }
                // 更新结果
                if (count == m)
                    ret.push_back(left);
            }
        }
        return ret;
    }
};

        int len = words[0].size(), m = words.size();
  • lenwords 中单词的长度,假设所有单词长度相同。mwords 中单词的数量。
  • words[0].size() 取得第一个单词的长度,words.size() 取得单词的数量。
            unordered_map<string, int> hash2; // 维护窗口内单词的频次

 这里又定义了一个 unordered_map<string, int> hash2,用于记录当前窗口内的单词频次。

                string in = s.substr(right, len);

 s.substr(right, len) 提取从 right 开始的 len 长度的子串,存储在 in 中。这是当前窗口中新进入的单词。

                if (right - left + 1 > len * m) {
                    string out = s.substr(left, len);
                    if (hash1.count(out) && hash2[out] <= hash1[out])
                        count--;
                    hash2[out]--;
                    left += len;
                }
  • right - left + 1 > len * m 判断窗口大小是否超过 words 中所有单词长度的总和。如果超过,需要缩小窗口。
  • s.substr(left, len) 提取窗口左端的单词,存储在 out 中。
  • 如果 outwords 中且 hash2[out] <= hash1[out],说明 count 中的一个有效匹配即将被移除,所以 count--
  • hash2[out]--hash2 中移除 out,减少其频次。
  • left += len 将窗口左边界右移一个单词的长度。

2.C语言代码 

typedef struct {
    char* word;
    int count;
} WordCount;

int* findSubstring(char* s, char** words, int wordsSize, int* returnSize) {
    int sLen = strlen(s);
    int wordLen = strlen(words[0]);
    int windowLen = wordLen * wordsSize;

    WordCount* hash1 = (WordCount*)malloc(wordsSize * sizeof(WordCount));
    for (int i = 0; i < wordsSize; i++) {
        hash1[i].word = words[i];
        hash1[i].count = 0;
    }

    // 计算words数组中每个单词的频次
    for (int i = 0; i < wordsSize; i++) {
        int found = 0;
        for (int j = 0; j < i; j++) {
            if (strcmp(words[i], hash1[j].word) == 0) {
                hash1[j].count++;
                found = 1;
                break;
            }
        }
        if (!found) {
            hash1[i].count = 1;
        }
    }

    int* ret = (int*)malloc(sLen * sizeof(int));
    *returnSize = 0;

    for (int i = 0; i < wordLen; i++) {
        WordCount* hash2 = (WordCount*)calloc(wordsSize, sizeof(WordCount));
        for (int j = 0; j < wordsSize; j++) {
            hash2[j].word = hash1[j].word;
        }

        int left = i, right = i, count = 0;
        while (right + wordLen <= sLen) {
            char* in = (char*)malloc((wordLen + 1) * sizeof(char));
            strncpy(in, s + right, wordLen);
            in[wordLen] = '\0';

            int foundInHash1 = 0;
            for (int j = 0; j < wordsSize; j++) {
                if (strcmp(in, hash2[j].word) == 0) {
                    hash2[j].count++;
                    foundInHash1 = 1;
                    if (hash2[j].count <= hash1[j].count) {
                        count++;
                    }
                    break;
                }
            }

            while (right - left + 1 > windowLen) {
                char* out = (char*)malloc((wordLen + 1) * sizeof(char));
                strncpy(out, s + left, wordLen);
                out[wordLen] = '\0';

                for (int j = 0; j < wordsSize; j++) {
                    if (strcmp(out, hash2[j].word) == 0) {
                        if (hash2[j].count <= hash1[j].count) {
                            count--;
                        }
                        hash2[j].count--;
                        break;
                    }
                }

                free(out);
                left += wordLen;
            }

            if (count == wordsSize) {
                ret[*returnSize] = left;
                (*returnSize)++;
            }

            free(in);
            right += wordLen;
        }
        free(hash2);
    }

    free(hash1);
    ret = realloc(ret, (*returnSize) * sizeof(int)); // 重新调整大小
    return ret;
}

4.C++知识点 

1. std::vector

  • 定义std::vector是C++标准模板库(STL)中的动态数组容器,提供了动态调整大小的功能。
  • 特点
    • 动态大小:可以根据需求自动调整大小。
    • 随机访问:支持高效的随机访问,可以通过索引直接访问任意元素。
    • 自动内存管理:自动管理内存的分配和释放。
  • 常用函数
    • push_back(value): 在末尾添加一个元素。
    • pop_back(): 删除末尾的元素。
    • size(): 返回当前元素的个数。
    • operator[]: 通过索引访问元素。

std::vector 是一个动态数组,提供了可以动态调整大小的数组实现。以下是如何声明、初始化和操作std::vector的示例:

#include <vector>
#include <iostream>

int main() {
    // 创建一个空的int类型vector
    std::vector<int> numbers;

    // 添加元素
    numbers.push_back(1);
    numbers.push_back(2);
    numbers.push_back(3);

    // 访问元素
    std::cout << "First element: " << numbers[0] << std::endl;

    // 迭代访问元素
    std::cout << "All elements:";
    for (size_t i = 0; i < numbers.size(); ++i) {
        std::cout << " " << numbers[i];
    }
    std::cout << std::endl;

    return 0;
}

 

用法解释

  • push_back: 在vector的末尾添加元素。
  • size(): 返回vector中元素的数量。
  • operator[]: 通过索引访问vector中的元素。

2. std::string

  • 定义std::string是C++标准库中的字符串类,用于处理字符序列。
  • 特点
    • 动态大小:可以根据需求自动调整大小。
    • 丰富的成员函数:提供了如查找、替换、获取子串等多种操作。
    • 安全性:相比C风格字符串,std::string更安全,避免了缓冲区溢出问题。
  • 常用函数
    • size(): 返回字符串的长度。
    • substr(pos, len): 提取子字符串。
    • find(sub): 查找子字符串的位置。

std::string 提供了一种处理字符串的方式,以下是std::string的基本用法:

#include <string>
#include <iostream>

int main() {
    // 创建字符串
    std::string greeting = "Hello, world!";

    // 获取字符串长度
    std::cout << "Length: " << greeting.size() << std::endl;

    // 获取子串
    std::string sub = greeting.substr(7, 5);
    std::cout << "Substring: " << sub << std::endl;

    // 拼接字符串
    std::string newGreeting = greeting + " Welcome!";
    std::cout << "New greeting: " << newGreeting << std::endl;

    return 0;
}

 

用法解释

  • size(): 返回字符串的长度。
  • substr(pos, len): 返回从pos位置开始、长度为len的子串。
  • operator+: 拼接两个字符串。

3. std::unordered_map

  • 定义std::unordered_map是C++11标准引入的哈希表容器,用于存储键值对,支持快速查找。
  • 特点
    • 无序存储:元素没有特定的顺序。
    • 哈希表实现:利用哈希函数实现快速的插入、删除和查找操作。
    • 复杂度:平均情况下,查找、插入、删除操作的时间复杂度为$O(1)$。
  • 常用函数
    • operator[]: 通过键访问或插入元素。
    • find(key): 查找键是否存在。
    • count(key): 返回键的出现次数(0或1)。

std::unordered_map 提供了键值对的存储,并支持快速查找:

#include <unordered_map>
#include <iostream>

int main() {
    // 创建一个unorderd_map,键是string,值是int
    std::unordered_map<std::string, int> fruitCount;

    // 插入元素
    fruitCount["apple"] = 3;
    fruitCount["banana"] = 5;
    fruitCount["orange"] = 2;

    // 查找元素
    std::cout << "Number of apples: " << fruitCount["apple"] << std::endl;

    // 迭代访问元素
    for (const auto& pair : fruitCount) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

 

用法解释

  • operator[]: 通过键访问或插入元素。
  • 迭代器:使用范围循环遍历unordered_map中的键值对。

4. 迭代器

  • 定义:迭代器是一种对象,提供对容器元素的遍历功能。几乎所有STL容器都提供迭代器支持。
  • 特点
    • 统一接口:提供统一的遍历容器元素的方式,无需关注容器的内部实现。
    • 类型:包括输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器。
  • 常用函数
    • begin(): 返回指向容器第一个元素的迭代器。
    • end(): 返回指向容器末尾后一个位置的迭代器。

迭代器用于遍历容器中的元素。以下是使用迭代器遍历std::vector的示例:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // 使用迭代器遍历vector
    for (std::vector<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}

 

用法解释

  • begin(): 返回指向容器第一个元素的迭代器。
  • end(): 返回指向容器末尾后一个位置的迭代器。
  • *it: 解引用迭代器,访问当前元素。

5. 范围循环 (range-based for loop)

  • 定义:C++11引入的语法糖,简化了对容器的遍历。
  • 特点
    • 简洁:无需显式管理迭代器。
    • 类型自动推导:使用auto关键字自动推导元素类型。

范围循环提供了一种简洁的方式遍历容器中的元素:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // 使用范围循环遍历vector
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

 

用法解释

  • for (int num : numbers): 直接访问numbers中的每个元素,num是元素的副本。

6. 动态内存管理

  • 定义:C++允许程序在运行时动态分配和释放内存。
  • 特点
    • 手动管理:需要手动分配和释放内存,避免内存泄漏。
    • 相关操作
      • new:分配内存。
      • delete:释放内存。
  • 智能指针:C++11引入了智能指针如std::unique_ptrstd::shared_ptr,帮助自动管理内存。

C++允许使用newdelete进行动态内存管理,以下是一个基本示例:

#include <iostream>

int main() {
    // 动态分配一个int类型的内存空间
    int* p = new int;
    *p = 5;
    std::cout << "Value: " << *p << std::endl;

    // 释放内存
    delete p;

    // 动态分配一个int数组
    int* arr = new int[3];
    arr[0] = 1;
    arr[1] = 2;
    arr[2] = 3;

    // 打印数组内容
    for (int i = 0; i < 3; ++i) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;

    // 释放数组内存
    delete[] arr;

    return 0;
}

 

用法解释

  • new: 动态分配内存。
  • delete: 释放内存。
  • delete[]: 释放动态分配的数组内存。

7. 面向对象编程(OOP)

  • 定义:面向对象编程是一种编程范式,使用类和对象进行抽象和封装。
  • :类是对对象的抽象描述,封装了数据和行为。
  • 对象:对象是类的实例,通过类定义的结构创建。
  • 访问修饰符
    • public: 公有成员,可以从类的外部访问。
    • private: 私有成员,不能从类的外部访问。
    • protected: 受保护成员,只能在类内部和派生类中访问。

C++支持面向对象编程,以下是一个简单的类定义和使用的示例:

#include <iostream>
#include <string>

class Dog {
private:
    std::string name;
    int age;

public:
    // 构造函数
    Dog(std::string n, int a) : name(n), age(a) {}

    // 成员函数
    void bark() {
        std::cout << "Woof! My name is " << name << " and I am " << age << " years old." << std::endl;
    }

    // 访问器函数
    std::string getName() const {
        return name;
    }

    int getAge() const {
        return age;
    }

    // 修改器函数
    void setName(std::string n) {
        name = n;
    }

    void setAge(int a) {
        age = a;
    }
};

int main() {
    // 创建对象
    Dog myDog("Buddy", 5);

    // 调用成员函数
    myDog.bark();

    // 修改属性
    myDog.setName("Charlie");
    myDog.setAge(6);
    myDog.bark();

    return 0;
}

 

用法解释

  • class: 定义一个类。
  • 访问修饰符private, public
  • 构造函数:用于初始化对象。
  • 成员函数:定义对象的行为。
  • 访问器函数和修改器函数:用于获取和设置对象的属性。

总结

标准库容器如std::vectorstd::unordered_map、字符串操作、迭代器、范围循环、动态内存管理以及面向对象编程(OOP)。通过这些示例,展示了如何使用C++的这些特性来高效、安全地处理数据和管理内存,编写可维护的代码。理解和掌握这些概念是编写优质C++程序的基础。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1961055.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

0基础学会无代码在亚马逊云科技AWS上利用LLM和智慧体(Agent)开发客服机器人

简介&#xff1a; 小李哥将继续每天介绍一个基于亚马逊云科技AWS云计算平台的全球前沿AI技术解决方案&#xff0c;帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS AI最佳实践&#xff0c;并应用到自己的日常工作里。 本次介绍的是如何利用亚马逊云科技大模型托管服…

【解决方法】git clone出现 curl 56 OpenSSL SSL_read: Connection was reset, errno 10054

当我们克隆别人的项目出现以下提示信息时候 remote: Enumerating objects: 7095, done. error: RPC failed; curl 56 OpenSSL SSL_read: Connection was reset, errno 10054 error: 2292 bytes of body are still expected fetch-pack: unexpected disconnect while reading s…

Python(C++)大尺度分层边值时变图统计推理并行算法

&#x1f3af;要点 &#x1f3af;分层结构制定生成模型 | &#x1f3af;贝叶斯模型选择程序 | &#x1f3af;分层结构图的信息性 | &#x1f3af;分层模型适应实值边协变量的网络 | &#x1f3af;分层模型适应时变网络&#xff0c;划分层对应于检测变化点 | &#x1f3af;定义…

Python中15个让你代码更优雅的上下文管理器用法

文末赠免费精品编程资料~~ 今天&#xff0c;我们要探索的是Python中一个超级实用又往往被低估的特性——上下文管理器。这可不是普通的魔法&#xff0c;它能让你的代码更加整洁、安全&#xff0c;还能自动处理资源&#xff0c;就像变魔术一样。准备好&#xff0c;让我们一起揭…

一分多行列转换(Gbase版)

1、源数据表结构 CREATE TABLE "sf_ref_pd_config" ("I_BATCH_NO" decimal(5,0) DEFAULT NULL COMMENT 批次ID,"V_ASSET_CLASS_NAME" varchar(200) DEFAULT NULL COMMENT 资产类型,"N_EXEC_ID" decimal(5,0) DEFAULT NULL COMMENT 执…

pyjwt:Python 中的 JWT 处理专家

文章目录 探索 pyjwt&#xff1a;Python 中的 JWT 处理专家简介&#xff1a;为何选择 pyjwt&#xff1f;什么是 pyjwt&#xff1f;安装 pyjwtpyjwt 的基本使用1. 编码JWT2. 解码JWT3. 验证签名4. 过期时间5. 自定义头部 场景应用场景一&#xff1a;用户登录场景二&#xff1a;A…

深度学习的数据类型总结

文章目录 1.基本概念1.比特&#xff08;bit&#xff09;&#xff1a;2. **字节**&#xff08;Byte&#xff09;:2. **数据类型**: 2. 相互转化 1.基本概念 1.比特&#xff08;bit&#xff09;&#xff1a; 计算机中最小的数据存储单位&#xff0c;用 0 和 1 表示。信息传输速…

python制作一个AI聊天机器人-【python教程】教你用python制作一个AI聊天机器人,源码可分享,零基础也能实现!

要制作一个基本的AI聊天机器人&#xff0c;我们可以使用Python的多个库&#xff0c;其中一个非常流行且易于上手的是Rasa。一个简单的入门示例&#xff0c;我们可以使用pyttsx3&#xff0c;是一个Python库&#xff0c;用于将文本转换为语音。 它提供了一个简单易用的接口&#…

【ThingsBoard初体验】本地运行源码踩坑记录

前言 运行源码之前&#xff0c;请先编译源码。这很重要&#xff01;&#xff01;&#xff01; 官网源码编译教程&#xff1a;http://www.ithingsboard.com/docs/user-guide/contribution/yuanmabianyi/ 如果编译过程中出现报错&#xff0c;请看我上一篇文章&#xff1a;【Thing…

《LeetCode热题100》---<滑动窗口篇两道>

本篇博客讲解LeetCode热题100道滑动窗口篇中的两道题 第一道&#xff1a;无重复字符的最长子串 第二道&#xff1a;找到字符当中的所有字母异位词 第一道&#xff1a;无重复字符的最长子串 哈希滑动窗口 class Solution {public int lengthOfLongestSubstring(String s0) {int…

nginx出现Refused to apply inline style because it violates

Content Security Policy的错误。根据错误提示&#xff0c;nginx拒绝应用内联样式&#xff0c;因为它违反了内容安全策略&#xff08;Content Security Policy&#xff09;。内容安全策略是一种浏览器机制&#xff0c;用于防止潜在的安全漏洞&#xff0c;通过限制从外部来源加载…

初步入门C ++之继承的概念

继承 ​ 继承&#xff0c;他的功能就如同他的名字一样&#xff0c;可以继承一个类的数据和方法&#xff0c;然后增添一些自己独有的数据和方法&#xff1a; 根据我们之前讲解初步入门C之类的例子&#xff0c;假如我们现在有一个长方体的类&#xff0c;它和长方型类唯一不一样…

屏幕录制与视频编辑的新纪元Camtasia Studio 2024

在数字化时代&#xff0c;视频已成为我们日常工作和生活中不可或缺的一部分。无论是教育、培训、营销还是娱乐&#xff0c;高质量的视频内容都发挥着至关重要的作用。而提到屏幕录制和视频编辑软件&#xff0c;Camtasia Studio无疑是一个家喻户晓的名字。随着2024年新版本的发布…

【Vue3】自定义组件

【Vue3】自定义组件 背景简介开发环境开发步骤及源码 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的日子。本文…

Java基本语法学习的案例练习

本文是在学习过C语言后&#xff0c;开始进行Java学习时&#xff0c;对于基本语法的一些案例练习。案例内容来自B站黑马编程课 1.HelloWorld 问题介绍;请编写程序输出“HelloWorld”. public class HelloWorld { public static void main(String[] args) { System.out.print…

树状机器学习模型综述(含python代码)

树状机器学习模型综述 树状模型是机器学习中一种非常重要的算法类别&#xff0c;因其直观的结构和良好的可解释性而广泛应用于分类和回归任务。本文将综述几种常见的树状模型&#xff0c;包括决策树、随机森林、LightGBM、XGBoost和CatBoost&#xff0c;讨论它们的原理、用途以…

高品质定制线缆知名智造品牌推荐-精工电联:高压线缆行业定制服务的领航者

定制线缆源头厂家推荐-精工电联&#xff1a;高压线缆行业定制服务的领航者 在当今这个高度信息化的社会&#xff0c;电力传输与分配系统的稳定运行至关重要。作为连接各个电力设备的纽带&#xff0c;高压线缆的质量直接关系到电力系统的安全性和稳定性。在定制高压线缆行业中&a…

【RK3568】点亮eDP屏幕+双屏异显

一、驱动eDP屏幕 一般来说&#xff0c;屏幕的规格书中会找到屏幕的相关参数&#xff0c;如没有&#xff0c;也可直接找屏幕厂商要&#xff0c;首先打开屏幕的规格书&#xff0c;搜索EDID Table&#xff0c;可找到如下信息&#xff1a; &#xff08;1&#xff09;显示时序配置 将…

越是熟人之间,越要注意这三个方面

不管什么时候&#xff0c;不管与谁相处&#xff0c;社交的边界和底线永远都是不变的。 对待陌生人的时候&#xff0c;我们总会按照既定的章法和礼节行事&#xff0c;可是在对待熟人的时候&#xff0c;很多人却忘了这些章法和礼节。虽然彼此熟悉了&#xff0c;不需要那么在乎章…