Tìm lần xuất hiện thứ n của chuỗi con trong một chuỗi


118

Điều này có vẻ như nó sẽ khá tầm thường, nhưng tôi là người mới sử dụng Python và muốn làm điều đó theo cách Pythonic nhất.

Tôi muốn tìm chỉ số tương ứng với lần xuất hiện thứ n của một chuỗi con trong một chuỗi.

Phải có một cái gì đó tương đương với những gì TÔI MUỐN làm, đó là

mystring.find("substring", 2nd)

Làm thế nào bạn có thể đạt được điều này trong Python?


7
Tìm lần xuất hiện thứ n của chuỗi? Tôi cho rằng nó có nghĩa là chỉ số của lần xuất hiện thứ n?
Đánh dấu Byers

2
Có, chỉ số của lần xuất hiện thứ n
prestomation

9
Điều gì sẽ xảy ra nếu có các trận đấu trùng lặp? Nên find_nth ('aaaa', 'aa', 2) trả về 1 hay 2?
Đánh dấu Byers

Đúng! cần phải có một cái gì đó để tìm lần xuất hiện thứ n của một chuỗi con trong một chuỗi và để tách chuỗi ở lần xuất hiện thứ n của một chuỗi con.
Reman

Câu trả lời:


69

Tôi nghĩ rằng cách tiếp cận lặp đi lặp lại của Mark sẽ là cách thông thường.

Đây là một giải pháp thay thế với phân tách chuỗi, thường có thể hữu ích cho việc tìm kiếm các quy trình liên quan:

def findnth(haystack, needle, n):
    parts= haystack.split(needle, n+1)
    if len(parts)<=n+1:
        return -1
    return len(haystack)-len(parts[-1])-len(needle)

Và đây là một cách nhanh chóng (và hơi bẩn, ở chỗ bạn phải chọn một số miếng lót không thể khớp với kim) một lớp lót:

'foo bar bar bar'.replace('bar', 'XXX', 1).find('bar')

7
Đề xuất đầu tiên sẽ rất kém hiệu quả đối với các chuỗi lớn khi trận đấu bạn quan tâm gần bắt đầu. Nó luôn luôn nhìn vào toàn bộ chuỗi. Thật thông minh nhưng tôi sẽ không giới thiệu điều này cho những người mới sử dụng Python và chỉ muốn tìm hiểu một cách tốt để làm điều đó.
Đánh dấu Byers

3
Cảm ơn, tôi thích một lớp lót của bạn. Tôi không nghĩ rằng đó là điều ngay lập tức có thể đọc được hầu hết trên thế giới, nhưng nó không phải là tồi tệ hơn sau đó hầu hết những người khác bên dưới
prestomation

1
+1 cho một lớp lót, điều này sẽ giúp tôi ngay bây giờ. Tôi đã nghĩ đến việc làm tương tự .rfind('XXX'), nhưng điều đó sẽ sụp đổ nếu 'XXX'xuất hiện muộn hơn trong đầu vào.
Nikhil Chelliah,

Hàm này giả sử n = 0, 1, 2, 3, ... Sẽ rất tuyệt khi bạn giả sử n = 1, 2, 3, 4, ...
Chúc bạn vui vẻ

75

Đây là một phiên bản Pythonic khác của giải pháp lặp lại đơn giản:

def find_nth(haystack, needle, n):
    start = haystack.find(needle)
    while start >= 0 and n > 1:
        start = haystack.find(needle, start+len(needle))
        n -= 1
    return start

Thí dụ:

>>> find_nth("foofoofoofoo", "foofoo", 2)
6

Nếu bạn muốn tìm lần xuất hiện chồng chéo thứ n của needle, bạn có thể tăng lên 1thay vì len(needle), như sau:

def find_nth_overlapping(haystack, needle, n):
    start = haystack.find(needle)
    while start >= 0 and n > 1:
        start = haystack.find(needle, start+1)
        n -= 1
    return start

Thí dụ:

>>> find_nth_overlapping("foofoofoofoo", "foofoo", 2)
3

Điều này dễ đọc hơn phiên bản của Mark và nó không yêu cầu thêm bộ nhớ của phiên bản tách hoặc nhập mô-đun biểu thức chính quy. Nó cũng tuân thủ một số quy tắc trong Zen of python , không giống như các recách tiếp cận khác nhau :

  1. Đơn giản là tốt hơn phức tạp.
  2. Phẳng hơn là lồng nhau.
  3. Số lượng khả năng đọc.

Điều này có thể được thực hiện trong một chuỗi không? Giống như find_nth (df.mystring.str, ('x'), 2) để tìm vị trí của phiên bản thứ 2 của 'x'?
Arthur D. Howland

36

Điều này sẽ tìm thấy lần xuất hiện thứ hai của chuỗi con trong chuỗi.

def find_2nd(string, substring):
   return string.find(substring, string.find(substring) + 1)

Chỉnh sửa: Tôi chưa nghĩ nhiều về hiệu suất, nhưng một đệ quy nhanh có thể giúp tìm ra lần xuất hiện thứ n:

def find_nth(string, substring, n):
   if (n == 1):
       return string.find(substring)
   else:
       return string.find(substring, find_nth(string, substring, n - 1) + 1)

Điều này có thể được mở rộng một cách tổng quát để tìm phần tử thứ n không?
ifly

Đây là câu trả lời hay nhất IMHO, tôi đã thực hiện một bổ sung nhỏ cho trường hợp đặc biệt trong đó n = 0
Jan Wilmans

Tôi không muốn chỉnh sửa bài viết cho ngắn gọn. Tuy nhiên, tôi đồng ý với bạn rằng n = 0 nên được coi là một trường hợp đặc biệt.
Sriram Murali

Điều này cần được điều chỉnh để xử lý trong trường hợp có ít hơn số nlần xuất hiện của chuỗi con. (Trong trường hợp này, giá trị trả về sẽ quay vòng theo chu kỳ qua tất cả các vị trí xuất hiện).
coldfix

29

Hiểu rằng regex không phải lúc nào cũng là giải pháp tốt nhất, tôi có thể sử dụng một giải pháp ở đây:

>>> import re
>>> s = "ababdfegtduab"
>>> [m.start() for m in re.finditer(r"ab",s)]
[0, 2, 11]
>>> [m.start() for m in re.finditer(r"ab",s)][2] #index 2 is third occurrence 
11

4
Tất nhiên, rủi ro ở đây là chuỗi cần tìm kiếm sẽ chứa các ký tự đặc biệt khiến regex làm điều gì đó mà bạn không muốn. Sử dụng re.escape sẽ giải quyết được điều này.
Đánh dấu Byers

1
Điều này thật thông minh, nhưng nó có thực sự là Pythonic? Có vẻ như quá mức cần thiết cho việc chỉ tìm lần xuất hiện thứ n của một chuỗi con và nó không chính xác dễ đọc. Ngoài ra, như bạn nói, bạn phải nhập khẩu tất cả lại cho điều này
Todd Gamblin

Khi bạn sử dụng dấu ngoặc vuông, bạn yêu cầu Python tạo toàn bộ danh sách. Dấu ngoặc tròn sẽ lặp chỉ thông qua các yếu tố đầu tiên, đó là hiệu quả hơn:(m.start() for m in re.finditer(r"ab",s))[2]
emu

1
@emu Không, những gì bạn đã đăng sẽ không hoạt động; bạn không thể lấy một chỉ mục của một trình tạo.
Mark Amery

@MarkAm rất xin lỗi! Tôi khá ngạc nhiên tại sao tôi lại đăng mã đó. Tuy nhiên, một giải pháp tương tự và xấu xí là có thể sử dụng các itertools.islicechức năng:next(islice(re.finditer(r"ab",s), 2, 2+1)).start()
emu

17

Tôi đang cung cấp một số kết quả đo điểm chuẩn so sánh các phương pháp tiếp cận nổi bật nhất được trình bày cho đến nay, cụ thể là @ bobince's findnth()(dựa trên str.split()) so với @ tgamblin's hoặc @Mark Byers ' find_nth()(dựa trên str.find()). Tôi cũng sẽ so sánh với phần mở rộng C ( _find_nth.so) để xem chúng ta có thể đi nhanh như thế nào. Đây là find_nth.py:

def findnth(haystack, needle, n):
    parts= haystack.split(needle, n+1)
    if len(parts)<=n+1:
        return -1
    return len(haystack)-len(parts[-1])-len(needle)

def find_nth(s, x, n=0, overlap=False):
    l = 1 if overlap else len(x)
    i = -l
    for c in xrange(n + 1):
        i = s.find(x, i + l)
        if i < 0:
            break
    return i

Tất nhiên, hiệu suất quan trọng nhất nếu chuỗi lớn, vì vậy giả sử chúng ta muốn tìm dòng mới thứ 1000001 ('\ n') trong tệp 1,3 GB được gọi là 'bigfile'. Để tiết kiệm bộ nhớ, chúng tôi muốn làm việc trên một mmap.mmapbiểu diễn đối tượng của tệp:

In [1]: import _find_nth, find_nth, mmap

In [2]: f = open('bigfile', 'r')

In [3]: mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)

Đã có vấn đề đầu tiên với findnth(), vì mmap.mmapcác đối tượng không hỗ trợ split(). Vì vậy, chúng tôi thực sự phải sao chép toàn bộ tệp vào bộ nhớ:

In [4]: %time s = mm[:]
CPU times: user 813 ms, sys: 3.25 s, total: 4.06 s
Wall time: 17.7 s

Oái oăm! May mắn thay, svẫn phù hợp với bộ nhớ 4 GB của Macbook Air của tôi, vì vậy hãy điểm chuẩn findnth():

In [5]: %timeit find_nth.findnth(s, '\n', 1000000)
1 loops, best of 3: 29.9 s per loop

Rõ ràng là một hiệu suất khủng khiếp. Hãy xem cách tiếp cận dựa trên str.find()làm như thế nào:

In [6]: %timeit find_nth.find_nth(s, '\n', 1000000)
1 loops, best of 3: 774 ms per loop

Tốt hơn nhiều! Rõ ràng, findnth()vấn đề của nó là nó buộc phải sao chép chuỗi trong suốt split(), đây đã là lần thứ hai chúng tôi sao chép 1,3 GB dữ liệu sau đó s = mm[:]. Ở đây có lợi thế thứ hai là find_nth(): Chúng tôi có thể sử dụng nó mmtrực tiếp, sao cho không yêu cầu bản sao của tệp:

In [7]: %timeit find_nth.find_nth(mm, '\n', 1000000)
1 loops, best of 3: 1.21 s per loop

Có vẻ như có một hình phạt hiệu suất nhỏ khi hoạt động mmso với s, nhưng điều này minh họa find_nth()có thể giúp chúng ta có câu trả lời trong 1,2 giây so với findnthtổng số 47 giây của.

Tôi không tìm thấy trường hợp nào mà str.find()phương pháp dựa trên tệ hơn đáng kể so với str.split()phương pháp dựa trên, vì vậy tại thời điểm này, tôi sẽ tranh luận rằng câu trả lời của @ tgamblin hoặc @Mark Byers nên được chấp nhận thay vì của @ bobince.

Trong thử nghiệm của tôi, phiên bản find_nth()trên là giải pháp Python thuần túy nhanh nhất mà tôi có thể nghĩ ra (rất giống với phiên bản của @Mark Byers). Hãy xem chúng ta có thể làm tốt hơn thế nào với mô-đun mở rộng C. Đây là _find_nthmodule.c:

#include <Python.h>
#include <string.h>

off_t _find_nth(const char *buf, size_t l, char c, int n) {
    off_t i;
    for (i = 0; i < l; ++i) {
        if (buf[i] == c && n-- == 0) {
            return i;
        }
    }
    return -1;
}

off_t _find_nth2(const char *buf, size_t l, char c, int n) {
    const char *b = buf - 1;
    do {
        b = memchr(b + 1, c, l);
        if (!b) return -1;
    } while (n--);
    return b - buf;
}

/* mmap_object is private in mmapmodule.c - replicate beginning here */
typedef struct {
    PyObject_HEAD
    char *data;
    size_t size;
} mmap_object;

typedef struct {
    const char *s;
    size_t l;
    char c;
    int n;
} params;

int parse_args(PyObject *args, params *P) {
    PyObject *obj;
    const char *x;

    if (!PyArg_ParseTuple(args, "Osi", &obj, &x, &P->n)) {
        return 1;
    }
    PyTypeObject *type = Py_TYPE(obj);

    if (type == &PyString_Type) {
        P->s = PyString_AS_STRING(obj);
        P->l = PyString_GET_SIZE(obj);
    } else if (!strcmp(type->tp_name, "mmap.mmap")) {
        mmap_object *m_obj = (mmap_object*) obj;
        P->s = m_obj->data;
        P->l = m_obj->size;
    } else {
        PyErr_SetString(PyExc_TypeError, "Cannot obtain char * from argument 0");
        return 1;
    }
    P->c = x[0];
    return 0;
}

static PyObject* py_find_nth(PyObject *self, PyObject *args) {
    params P;
    if (!parse_args(args, &P)) {
        return Py_BuildValue("i", _find_nth(P.s, P.l, P.c, P.n));
    } else {
        return NULL;    
    }
}

static PyObject* py_find_nth2(PyObject *self, PyObject *args) {
    params P;
    if (!parse_args(args, &P)) {
        return Py_BuildValue("i", _find_nth2(P.s, P.l, P.c, P.n));
    } else {
        return NULL;    
    }
}

static PyMethodDef methods[] = {
    {"find_nth", py_find_nth, METH_VARARGS, ""},
    {"find_nth2", py_find_nth2, METH_VARARGS, ""},
    {0}
};

PyMODINIT_FUNC init_find_nth(void) {
    Py_InitModule("_find_nth", methods);
}

Đây là setup.pytệp:

from distutils.core import setup, Extension
module = Extension('_find_nth', sources=['_find_nthmodule.c'])
setup(ext_modules=[module])

Cài đặt như bình thường với python setup.py install. Mã C có lợi thế ở đây vì nó bị giới hạn trong việc tìm các ký tự đơn lẻ, nhưng hãy xem tốc độ của nó như thế nào:

In [8]: %timeit _find_nth.find_nth(mm, '\n', 1000000)
1 loops, best of 3: 218 ms per loop

In [9]: %timeit _find_nth.find_nth(s, '\n', 1000000)
1 loops, best of 3: 216 ms per loop

In [10]: %timeit _find_nth.find_nth2(mm, '\n', 1000000)
1 loops, best of 3: 307 ms per loop

In [11]: %timeit _find_nth.find_nth2(s, '\n', 1000000)
1 loops, best of 3: 304 ms per loop

Rõ ràng là vẫn còn nhanh hơn một chút. Điều thú vị là không có sự khác biệt về mức C giữa trường hợp trong bộ nhớ và trường hợp được ánh xạ. Nó cũng là thú vị khi thấy rằng _find_nth2(), mà là dựa trên string.h's memchr()chức năng thư viện, phải thua thiệt so với thực hiện đơn giản trong _find_nth(): Các 'tối ưu hóa' bổ sung trong memchr()được rõ ràng lấp đầy sỏi ...

Tóm lại, việc triển khai trong findnth()(dựa trên str.split()) thực sự là một ý tưởng tồi, vì (a) nó hoạt động rất tệ đối với các chuỗi lớn hơn do yêu cầu sao chép và (b) nó hoàn toàn không hoạt động trên mmap.mmapcác đối tượng. Việc triển khai trong find_nth()(dựa trên str.find()) nên được ưu tiên trong mọi trường hợp (và do đó là câu trả lời được chấp nhận cho câu hỏi này).

Vẫn còn khá nhiều chỗ để cải thiện, vì phần mở rộng C chạy nhanh hơn gần như gấp 4 lần so với mã Python thuần túy, cho thấy rằng có thể có một trường hợp cho một hàm thư viện Python chuyên dụng.


8

Cách đơn giản nhất?

text = "This is a test from a test ok" 

firstTest = text.find('test')

print text.find('test', firstTest + 1)

Tôi có thể tưởng tượng rằng điều này cũng khá hiệu quả, so với các giải pháp khác.
Rotareti 19/09/17

7

Tôi có thể sẽ làm điều gì đó như thế này, sử dụng hàm find có tham số chỉ mục:

def find_nth(s, x, n):
    i = -1
    for _ in range(n):
        i = s.find(x, i + len(x))
        if i == -1:
            break
    return i

print find_nth('bananabanana', 'an', 3)

Tôi đoán nó không phải là Pythonic đặc biệt, nhưng nó đơn giản. Bạn có thể làm điều đó bằng cách sử dụng đệ quy để thay thế:

def find_nth(s, x, n, i = 0):
    i = s.find(x, i)
    if n == 1 or i == -1:
        return i 
    else:
        return find_nth(s, x, n - 1, i + len(x))

print find_nth('bananabanana', 'an', 3)

Đó là một cách chức năng để giải quyết nó, nhưng tôi không biết liệu điều đó có khiến nó trở nên Pythonic hay không.


1
for _ in xrange(n):có thể được sử dụng thay vìwhile n: ... n-=1
jfs

@JF Sebastian: Vâng, tôi đoán đó là Pythonic nhiều hơn một chút. Tôi sẽ cập nhật.
Đánh dấu Byers

BTW: xrange không còn cần thiết trong Python 3: jumpintopython3.org/…
Mark Byers

1
return find_nth(s, x, n - 1, i + 1)nên được return find_nth(s, x, n - 1, i + len(x)). Không phải là một vấn đề lớn, nhưng tiết kiệm một số thời gian tính toán.
Dan Loewenherz

@dlo: Trên thực tế, điều đó có thể cho các kết quả khác nhau trong một số trường hợp: find_nth ('aaaa', 'aa', 2). Của tôi cho 1, của bạn cho 2. Tôi đoán của bạn thực sự là những gì người đăng muốn. Tôi sẽ cập nhật mã của mình. Cảm ơn vì nhận xét.
Đánh dấu Byers

3

Điều này sẽ cung cấp cho bạn một mảng các chỉ số bắt đầu cho các kết quả phù hợp với yourstring:

import re
indices = [s.start() for s in re.finditer(':', yourstring)]

Sau đó, mục nhập thứ n của bạn sẽ là:

n = 2
nth_entry = indices[n-1]

Tất nhiên bạn phải cẩn thận với giới hạn chỉ số. Bạn có thể nhận được số lượng các trường hợp yourstringnhư thế này:

num_instances = len(indices)

2

Đây là một cách tiếp cận khác bằng cách sử dụng re.finditer.
Sự khác biệt là điều này chỉ nhìn vào đống cỏ khô nếu cần thiết

from re import finditer
from itertools import dropwhile
needle='an'
haystack='bananabanana'
n=2
next(dropwhile(lambda x: x[0]<n, enumerate(re.finditer(needle,haystack))))[1].start() 

2

Đây là một phiên bản re+ khác itertoolssẽ hoạt động khi tìm kiếm a strhoặc a RegexpObject. Tôi sẽ thoải mái thừa nhận rằng điều này có thể được thiết kế quá mức, nhưng vì một số lý do nó giúp tôi giải trí.

import itertools
import re

def find_nth(haystack, needle, n = 1):
    """
    Find the starting index of the nth occurrence of ``needle`` in \
    ``haystack``.

    If ``needle`` is a ``str``, this will perform an exact substring
    match; if it is a ``RegexpObject``, this will perform a regex
    search.

    If ``needle`` doesn't appear in ``haystack``, return ``-1``. If
    ``needle`` doesn't appear in ``haystack`` ``n`` times,
    return ``-1``.

    Arguments
    ---------
    * ``needle`` the substring (or a ``RegexpObject``) to find
    * ``haystack`` is a ``str``
    * an ``int`` indicating which occurrence to find; defaults to ``1``

    >>> find_nth("foo", "o", 1)
    1
    >>> find_nth("foo", "o", 2)
    2
    >>> find_nth("foo", "o", 3)
    -1
    >>> find_nth("foo", "b")
    -1
    >>> import re
    >>> either_o = re.compile("[oO]")
    >>> find_nth("foo", either_o, 1)
    1
    >>> find_nth("FOO", either_o, 1)
    1
    """
    if (hasattr(needle, 'finditer')):
        matches = needle.finditer(haystack)
    else:
        matches = re.finditer(re.escape(needle), haystack)
    start_here = itertools.dropwhile(lambda x: x[0] < n, enumerate(matches, 1))
    try:
        return next(start_here)[1].start()
    except StopIteration:
        return -1

2

Xây dựng trên câu trả lời của modle13 , nhưng không có sự rephụ thuộc của mô-đun.

def iter_find(haystack, needle):
    return [i for i in range(0, len(haystack)) if haystack[i:].startswith(needle)]

Tôi ước gì đây là một phương thức chuỗi nội trang.

>>> iter_find("http://stackoverflow.com/questions/1883980/", '/')
[5, 6, 24, 34, 42]

1
>>> s="abcdefabcdefababcdef"
>>> j=0
>>> for n,i in enumerate(s):
...   if s[n:n+2] =="ab":
...     print n,i
...     j=j+1
...     if j==2: print "2nd occurence at index position: ",n
...
0 a
6 a
2nd occurence at index position:  6
12 a
14 a

1

Cung cấp một giải pháp "khó khăn" khác, sử dụng splitjoin.

Trong ví dụ của bạn, chúng tôi có thể sử dụng

len("substring".join([s for s in ori.split("substring")[:2]]))

1
# return -1 if nth substr (0-indexed) d.n.e, else return index
def find_nth(s, substr, n):
    i = 0
    while n >= 0:
        n -= 1
        i = s.find(substr, i + 1)
    return i

cần một lời giải thích
Ctznkane525

find_nth('aaa', 'a', 0)trả lại 1trong khi nó sẽ trở lại 0. Bạn cần một cái gì đó giống như i = s.find(substr, i) + 1và sau đó trở lại i - 1.
a_guest

1

Giải pháp mà không cần sử dụng vòng lặp và đệ quy.

Sử dụng mẫu bắt buộc trong phương thức biên dịch và nhập lần xuất hiện mong muốn vào biến 'n' và câu lệnh cuối cùng sẽ in chỉ số bắt đầu của lần xuất hiện thứ n của mẫu trong chuỗi đã cho. Ở đây kết quả của finditer tức là trình lặp đang được chuyển đổi thành danh sách và truy cập trực tiếp vào chỉ mục thứ n.

import re
n=2
sampleString="this is history"
pattern=re.compile("is")
matches=pattern.finditer(sampleString)
print(list(matches)[n].span()[0])

1

Đối với trường hợp đặc biệt khi bạn tìm kiếm lần xuất hiện thứ n của một ký tự (tức là chuỗi con có độ dài 1), hàm sau hoạt động bằng cách xây dựng danh sách tất cả các vị trí xuất hiện của ký tự đã cho:

def find_char_nth(string, char, n):
    """Find the n'th occurence of a character within a string."""
    return [i for i, c in enumerate(string) if c == char][n-1]

Nếu có ít hơn số nlần xuất hiện của ký tự đã cho, nó sẽ cho IndexError: list index out of range.

Điều này bắt nguồn từ câu trả lời của @ Zv_oDD và được đơn giản hóa cho trường hợp của một ký tự duy nhất.


Điều này thật đẹp.
Hafiz Hilman Mohammad Sofian

0

Thay thế một lớp lót là tuyệt vời nhưng chỉ hoạt động vì XX và thanh có cùng một lớp lót

Một def tốt và chung chung sẽ là:

def findN(s,sub,N,replaceString="XXX"):
    return s.replace(sub,replaceString,N-1).find(sub) - (len(replaceString)-len(sub))*(N-1)

0

Đây là câu trả lời mà bạn thực sự muốn:

def Find(String,ToFind,Occurence = 1):
index = 0 
count = 0
while index <= len(String):
    try:
        if String[index:index + len(ToFind)] == ToFind:
            count += 1
        if count == Occurence:
               return index
               break
        index += 1
    except IndexError:
        return False
        break
return False

0

Đây là giải pháp của tôi để tìm sự xuất hiện nlần thứ của btrong chuỗi a:

from functools import reduce


def findNth(a, b, n):
    return reduce(lambda x, y: -1 if y > x + 1 else a.find(b, x + 1), range(n), -1)

Nó là Python thuần túy và lặp đi lặp lại. Đối với 0 hoặc nquá lớn, nó trả về -1. Nó là một lớp lót và có thể được sử dụng trực tiếp. Đây là một ví dụ:

>>> reduce(lambda x, y: -1 if y > x + 1 else 'bibarbobaobaotang'.find('b', x + 1), range(4), -1)
7

0

Def:

def get_first_N_words(mytext, mylen = 3):
    mylist = list(mytext.split())
    if len(mylist)>=mylen: return ' '.join(mylist[:mylen])

Để sử dụng:

get_first_N_words('  One Two Three Four ' , 3)

Đầu ra:

'One Two Three'

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.