Hướng dẫn write a javascript function to count the occurrence of a substring in a string - viết một hàm javascript để đếm sự xuất hiện của một chuỗi con trong một chuỗi

Đây là chức năng nhanh nhất!

Tại sao nó nhanh hơn?

  • Không kiểm tra char bởi char [với 1 ngoại lệ]
  • Sử dụng một thời gian và gia số 1 var [số char Count var] so với A để kiểm tra độ dài và tăng 2 vars [thường là var i và var với số lượng char]
  • Sử dụng cách ít vars
  • Không sử dụng regex!
  • Sử dụng chức năng [hy vọng] được tối ưu hóa cao
  • Tất cả các hoạt động đều được kết hợp như chúng có thể, tránh bị chậm lại do nhiều hoạt động

    String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};
    

Đây là phiên bản chậm và dễ đọc hơn:

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };

Cái này chậm hơn vì bộ đếm, tên var dài và lạm dụng 1 var.

Để sử dụng nó, bạn chỉ cần làm điều này:

    'The char "a" only shows up twice'.timesCharExist['a'];

Chỉnh sửa: [2013/12/16]

Đừng sử dụng với Opera 12.16 trở lên! Sẽ mất gần 2,5 lần so với giải pháp Regex!

Trên Chrome, giải pháp này sẽ mất từ ​​14ms đến 20ms cho 1.000.000 ký tự.

Giải pháp Regex mất 11-14ms cho cùng một lượng.

Sử dụng một hàm [bên ngoài

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
4] sẽ mất khoảng 10-13ms.

Đây là mã được sử dụng:

    String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};

    var x=Array[100001].join['1234567890'];

    console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];

    console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];

    var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};

    console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];

Kết quả của tất cả các giải pháp nên là 100.000!

Lưu ý: Nếu bạn muốn chức năng này đếm nhiều hơn 1 char, hãy thay đổi

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
5 thành
    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
6

Giới thiệu

Khi làm việc với các chuỗi hoặc một lượng lớn văn bản, có lẽ bạn sẽ gặp phải các tình huống mà bạn cần để đếm số lần một chuỗi con cụ thể xảy ra trong một chuỗi khác.

Trong bài viết này, chúng ta sẽ xem xét cách sử dụng JavaScript để đếm số lần xuất hiện trong chuỗi trong một chuỗi. Chúng tôi sẽ xem xét các phương pháp và phương pháp khác nhau để có được con số đó.

Nhưng trước khi chúng ta bắt đầu, trước tiên chúng ta hãy xác định nền tảng là gì.

Chất nền là gì?

Một chuỗi con là một chuỗi được xác định rõ ràng của các ký tự liên tiếp trong một chuỗi. Ví dụ: nếu chúng ta có chuỗi "Tên tôi là John Doe", thì "Tên là" là một phần phụ, nhưng "là tên" không phải vì nó không còn là một chuỗi liên tiếp [chúng ta đã thay đổi thứ tự của các từ] . Các từ riêng lẻ như "là" và "tên" luôn luôn là nền tảng.

Lưu ý: "Tên y là Jo" là một nền tảng hợp lệ của "Tên tôi là John Doe". Nói cách khác, không phải lúc nào cũng là toàn bộ từ, chúng có thể không dễ đọc hơn nhiều. "y name is Jo" is a valid substring of the "My name is John Doe" as well. In other words, substrings are not always whole words, they can be much less readable.

Có nhiều cách để thực hiện điều này trong JavaScript, nhưng hai phương pháp chính là phương pháp

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 và các biểu thức thông thường.

Đếm số lượng chuỗi con trong chuỗi với phương thức split []

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 là phương pháp JavaScript để chia chuỗi thành một mảng các chuỗi con trong khi bảo tồn chuỗi ban đầu. Phương thức này chấp nhận một dấu phân cách và tách một chuỗi dựa trên nó. Nếu không có dấu tách nào được cung cấp,
    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 sẽ trả về một mảng chỉ có một phần tử - chuỗi gốc.

LƯU Ý: Có lẽ ví dụ rõ ràng nhất của dấu phân cách là không gian trống. Khi bạn cung cấp nó dưới dạng phân tách cho phương thức

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7, chuỗi ban đầu sẽ được cắt giảm bất cứ khi nào một khoảng trống xảy ra. Do đó, phương thức
    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 sẽ trả về một mảng các từ riêng lẻ từ chuỗi gốc.
Probably the most obvious example of the separator is the blank space. When you provide it as a separator for the
    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 method, the original string will be sliced up whenever a blank space occurs. Therefore, the
    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 method will return an array of individual words from the original string.

Trong bài viết này, chúng tôi sẽ sử dụng một thủ thuật tiện dụng để có được số lần xuất hiện của một chuỗi con trong một chuỗi. Chúng tôi sẽ đặt chuỗi con thành Trình phân tách trong phương thức

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7. Bằng cách đó, chúng ta có thể trích xuất số lần xuất hiện của chuỗi con từ mảng mà phương thức
    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 đã trả về:

let myString = "John Doe has 5 oranges while Jane Doe has only 2 oranges, Jane gave Mike 1 of her orange so she is now left with only 1 Orange.";
let mySubString = "orange";

let count = myString.split[mySubString].length - 1;
console.log[count]; // 3

Mã trên đã trả về

    'The char "a" only shows up twice'.timesCharExist['a'];
4, nhưng
    'The char "a" only shows up twice'.timesCharExist['a'];
5 chỉ có một thể hiện của chuỗi "màu cam". Chúng ta hãy kiểm tra những gì đã xảy ra bằng cách kiểm tra mảng được tạo sau khi chúng ta chia chuỗi ban đầu với "màu cam" làm dấu tách:

console.log[myString.split[mySubString]];

Điều này sẽ cung cấp cho chúng tôi:

['John Doe has 5 ', 's which Jane Doe has only 2 ', 's, Jane gave Mike 1 of her ', ' so she is now left with only 1 Orange.']

Về cơ bản, phương pháp

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 đã loại bỏ tất cả các lần xuất hiện của chuỗi "màu cam" khỏi chuỗi ban đầu và cắt nó ở những nơi mà phần nền đã được loại bỏ.

Lưu ý: Lưu ý cách áp dụng cho chuỗi "Oranges" - "Orange" là chất nền của nó, do đó,

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 loại bỏ "màu cam" và chỉ để chúng tôi bằng "S". Notice how that applies to the string "oranges" - the "orange" is its substring, therefore,
    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 removes "orange" and leaves us only with "s".

Vì chúng tôi đã tìm thấy ba lần xuất hiện của chuỗi "Orange", chuỗi ban đầu đã được cắt ở ba nơi - do đó chúng tôi đã tạo ra bốn nền tảng. Đó là lý do tại sao chúng ta cần trừ

    'The char "a" only shows up twice'.timesCharExist['a'];
8 từ độ dài mảng khi chúng ta tính toán số lần xuất hiện của chuỗi con.

Đó là tất cả tốt, nhưng có thêm một màu cam trong chuỗi gốc - từ cuối cùng là "màu cam". Tại sao chúng ta không tính nó trong ví dụ trước? Đó là bởi vì phương pháp

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
7 nhạy cảm với trường hợp, do đó, nó coi "màu cam" và "màu cam" là các yếu tố khác nhau.

Nếu bạn cần làm cho mã hóa không nhạy cảm với mã của mình, trước tiên sẽ chuyển đổi toàn bộ chuỗi và chuỗi con thành một trường hợp văn bản cụ thể trước khi kiểm tra các lần xuất hiện:

let myString = "John Doe has 5 oranges while Jane Doe has only 2 oranges, Jane gave Mike 1 of her orange so she is now left with only 1 Orange.";
let mySubString = "ORANGE";

let myStringLC = myString.toLowerCase[];
let mySubStringLC = mySubString.toLowerCase[];

let count = myStringLC.split[mySubStringLC].length - 1;
console.log[]; // 4

Ngoài ra, điều cuối cùng chúng ta có thể làm là làm cho mã của chúng ta có thể tái sử dụng bằng cách gói nó với một chức năng:

const countOccurence = [string, word] => {
    let stringLC = string.toLowerCase[];
    let wordLC = word.toLowerCase[];
    
    let count = stringLC.split[wordLC].length - 1;
    
    return count
};

Đếm số lượng chuỗi con trong chuỗi với regex

Một phương pháp khác để đếm số lần xuất hiện là sử dụng các biểu thức chính quy [regex]. Chúng là các mẫu của các ký tự được sử dụng để tìm kiếm, khớp và xác thực chuỗi. Có lẽ trường hợp sử dụng phổ biến nhất cho các biểu thức thông thường là xác thực biểu mẫu - kiểm tra xem chuỗi có phải là email [hợp lệ], số điện thoại, v.v. một chuỗi.

Nếu bạn muốn biết thêm về các biểu thức thông thường trong JavaScript, bạn nên đọc hướng dẫn toàn diện của chúng tôi - "Hướng dẫn về các biểu thức thông thường và các chuỗi phù hợp trong JavaScript".

Trước hết, chúng ta cần xác định một biểu thức chính quy sẽ phù hợp với nền tảng mà chúng ta đang tìm kiếm. Giả sử chúng tôi muốn tìm số lần xuất hiện của chuỗi "màu cam" trong một chuỗi lớn hơn, biểu thức chính quy của chúng tôi sẽ trông như sau:

let regex = /orange/gi;

Kiểm tra hướng dẫn thực hành của chúng tôi, thực tế để học Git, với các thực hành tốt nhất, các tiêu chuẩn được công nghiệp chấp nhận và bao gồm bảng gian lận. Ngừng các lệnh git googling và thực sự tìm hiểu nó!

Trong JavaScript, chúng tôi viết một mẫu biểu thức chính quy giữa hai dấu gạch chéo về phía trước -

    String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};

    var x=Array[100001].join['1234567890'];

    console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];

    console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];

    var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};

    console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];
0. Tùy chọn, sau lần cắt chuyển tiếp thứ hai, bạn có thể đặt một danh sách các cờ - các ký tự đặc biệt được sử dụng để thay thế hành vi mặc định khi khớp các mẫu.

Ví dụ, theo mặc định, các biểu thức chính quy chỉ khớp với lần xuất hiện đầu tiên của mẫu trong chuỗi tìm kiếm. Ngoài ra, khớp là nhạy cảm trường hợp, có lẽ không phải là điều chúng tôi muốn khi tìm kiếm các chất nền. Do đó, chúng tôi sẽ giới thiệu hai lá cờ mà chúng tôi sẽ sử dụng cho mục đích của bài viết này:

  •     String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};
    
        var x=Array[100001].join['1234567890'];
    
        console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];
    
        console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];
    
        var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};
    
        console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];
    
    1 - Đảm bảo rằng chúng ta nhận được tất cả các lần xuất hiện của mẫu [không chỉ là mẫu đầu tiên]
  •     String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};
    
        var x=Array[100001].join['1234567890'];
    
        console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];
    
        console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];
    
        var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};
    
        console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];
    
    2 - Đảm bảo rằng phù hợp là không nhạy cảm với trường hợp

Lưu ý: Dựa trên nhu cầu của bạn, bạn có thể chọn những lá cờ bạn sẽ sử dụng. Đây không phải là bắt buộc. Based on your needs, you can choose what flags you will use. These are not mandatory.

Bây giờ, chúng ta hãy sử dụng biểu thức chính quy được tạo trước đó để đếm số lần xuất hiện của chuỗi "màu cam" trong

    'The char "a" only shows up twice'.timesCharExist['a'];
5:

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
0

Lưu ý: Chúng tôi đã thêm

    String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};

    var x=Array[100001].join['1234567890'];

    console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];

    console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];

    var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};

    console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];
4 để trả về một mảng trống nếu không có khớp. Do đó, số lần xuất hiện sẽ được đặt thành
    String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};

    var x=Array[100001].join['1234567890'];

    console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];

    console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];

    var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};

    console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];
5.
We've added
    String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};

    var x=Array[100001].join['1234567890'];

    console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];

    console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];

    var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};

    console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];
4 in returns an empty array if there is no match. Therefore, the number of occurrences will be set to
    String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};

    var x=Array[100001].join['1234567890'];

    console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];

    console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];

    var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};

    console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];
5.

Ngoài ra, chúng ta có thể sử dụng hàm tạo

    String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};

    var x=Array[100001].join['1234567890'];

    console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];

    console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];

    var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};

    console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];
6 để tạo biểu thức chính quy. Nó chấp nhận một mẫu tìm kiếm là đối số đầu tiên và cờ là thứ hai:

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
1

Ngoài ra, chúng ta có thể biến điều này trở nên có thể tái sử dụng bằng cách gói nó theo hàm tách biệt:

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
2

Phù hợp với các cụm từ chính xác

Đôi khi, bạn muốn khớp với một cụm từ hoặc từ nghiêm ngặt - để "cam" không được bao gồm trong số lượng của bạn, hoặc bất kỳ từ nào bao gồm "màu cam", nhưng không hoàn toàn là "màu cam". Đây là một trường hợp sử dụng cụ thể hơn là tìm kiếm các chuỗi trong chuỗi, và may mắn là khá dễ dàng!

    String.prototype.timesCharExist = function [ chr ] {
        var total = 0, last_location = 0, single_char = [ chr + '' ][0];
        while[ last_location = this.indexOf[ single_char, last_location ] + 1 ]
        {
            total = total + 1;
        }
        return total;
    };
3

Bằng cách kết thúc thuật ngữ của chúng tôi trong

    String.prototype.timesCharExist=function[c]{var t=0,l=0,c=[c+''][0];while[l=this.indexOf[c,l]+1]++t;return t};

    var x=Array[100001].join['1234567890'];

    console.time['proto'];x.timesCharExist['1'];console.timeEnd['proto'];

    console.time['regex'];x.match[/1/g].length;console.timeEnd['regex'];

    var timesCharExist=function[x,c]{var t=0,l=0,c=[c+''][0];while[l=x.indexOf[c,l]+1]++t;return t;};

    console.time['func'];timesCharExist[x,'1'];console.timeEnd['func'];
7, chúng tôi phù hợp với "màu cam" [không phân biệt chữ hoa chữ thường] và Regex này sẽ chỉ khớp hai lần trong câu của chúng tôi [cả "Oranges" đều không phù hợp].

Hiệu suất điểm chuẩn

Khi chúng tôi chạy cả hai phương thức bằng điểm chuẩn JS, phương thức chia sẽ luôn xuất hiện nhanh hơn phương pháp Regex, mặc dù điều này không thực sự đáng chú ý ngay cả đối với Corpora văn bản khá lớn. Có lẽ bạn sẽ ổn khi sử dụng.

Lưu ý: Không dựa vào các điểm chuẩn này như là quyết định cuối cùng của bạn. Thay vào đó, hãy tự mình kiểm tra chúng để xác định cái nào phù hợp nhất với trường hợp sử dụng cụ thể của bạn. Do not rely on these benchmarks as your final decision. Instead, test them out yourself to determine which one is the best fit for your specific use case.

Sự kết luận

Trong bài viết này, chúng tôi đã tìm hiểu về hai phương pháp tiêu chuẩn để tính toán số lần xuất hiện của các chuỗi con trong một chuỗi. Chúng tôi cũng đã đánh giá kết quả, lưu ý rằng nó không thực sự quan trọng bạn thực hiện cách tiếp cận nào miễn là nó hoạt động cho bạn.

Bài Viết Liên Quan

Chủ Đề